33 Commits

Author SHA1 Message Date
sindhu
4974e02956 step 32 : change alignmnet right total in history 2024-01-22 10:51:07 +07:00
sindhu
4e3d251169 step 31 : total history, change password, resize image from camera, update versi 1.2.0 2024-01-22 10:25:31 +07:00
sindhu
c67f771d25 step 30 : update versi 1.1.5 2024-01-19 12:14:11 +07:00
sindhu
2cab59c86c step 29 : add base 64 param in repository insert tx 2024-01-19 12:13:19 +07:00
sindhu
c2f43f9061 step 28 : add usertransaki home dan history 2024-01-19 10:44:49 +07:00
sindhu
60919d6d12 step 27 : fix homescreen, report timestamp, filter history transaksi 2024-01-18 14:18:25 +07:00
sindhu
7986d68560 change company dari andy 2024-01-17 17:05:08 +07:00
sindhu
774d70884a Merge branch 'sindhu/history_transaksi_v2_17_01_2024_14_26' 2024-01-17 16:24:48 +07:00
sindhu
c8f1d7a1c9 step 1 : copy file drawer, history confirm, dialog img, btn excel & pdf 2024-01-17 16:19:54 +07:00
sindhu
abddef179d login 2024-01-17 14:17:12 +07:00
sindhu
db88512ca9 commit Merge branch 'andy/merge_camera' 2024-01-17 14:00:58 +07:00
Sas Andy
15097451d2 fix error last rebase 2024-01-17 13:57:29 +07:00
sindhu
987a5a5c3a step 25 : ganti icon apk 2024-01-17 13:54:41 +07:00
Sas Andy
01e4fccb08 Merge branch 'andy/merge_camera' of https://devone.aplikasi.web.id/gitea/sindhu/app_petikes into andy/merge_camera 2024-01-17 13:43:58 +07:00
Sas Andy
431c4f0039 upload file & revisi ui 2024-01-17 13:42:38 +07:00
Sas Andy
578e296097 penggabungan browse file & pick picture dan pemecahan file 2024-01-17 13:41:46 +07:00
Sas Andy
1235fd9e10 coba camera 2024-01-17 13:41:17 +07:00
Sas Andy
824216701a init 2024-01-17 13:30:30 +07:00
Sas Andy
1696e47cd3 upload file & revisi ui 2024-01-17 13:27:45 +07:00
sindhu
88ad1d6584 step 24 : fix drawer, login bg color 2024-01-17 12:34:22 +07:00
sindhu
844e4c48f5 step 23 : home screen, repository, provider 2024-01-17 11:40:11 +07:00
sindhu
f3b941772b step 22 : build apk, setting logo 2024-01-17 09:47:34 +07:00
sindhu
be50c48607 step 21 : fix bug history transaksi, logo apk.png 2024-01-17 09:05:11 +07:00
Sas Andy
080b540d8f penggabungan browse file & pick picture dan pemecahan file 2024-01-17 08:32:11 +07:00
Sas Andy
81de9c2b58 coba camera 2024-01-17 08:02:09 +07:00
Sas Andy
ad265320e5 init 2024-01-17 08:00:43 +07:00
sindhu
f915ed2c10 step 20 : delete transaksi, bug icon svg from server, date convert tgl trans 2024-01-16 22:06:42 +07:00
sindhu
178dd3b509 step 19 : fix check svg from server 2024-01-16 20:18:40 +07:00
sindhu
9416c010d7 step 18 : history widget atas cmp 2024-01-16 18:02:38 +07:00
sindhu
de84b76170 step 17 : extract widget field row history transaksi 2024-01-16 11:46:17 +07:00
sindhu
198f7ccd17 step 16 : add M_CompanyID for trans, menu drawer, fix bug overflow btn 2024-01-16 10:38:44 +07:00
sindhu
a317946fc2 step 15 : remove padding report 2024-01-16 09:03:44 +07:00
sindhu
088c18f536 step 14 : fix userid transaksi post, typography, login, drawer 2024-01-16 08:03:25 +07:00
111 changed files with 8065 additions and 1345 deletions

View File

@@ -52,6 +52,7 @@ android {
targetSdkVersion flutter.targetSdkVersion
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
multiDexEnabled true
}
buildTypes {
@@ -69,4 +70,5 @@ flutter {
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'com.android.support:multidex:1.0.3'
}

View File

@@ -1,8 +1,14 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<application
android:label="app_petty_cash"
android:label="PettyCash"
android:name="${applicationName}"
android:icon="@mipmap/ic_launcher">
android:icon="@mipmap/launcher_icon"
android:usesCleartextTraffic="true"
android:requestLegacyExternalStorage="true"
>
<activity
android:name=".MainActivity"
android:exported="true"
@@ -10,7 +16,9 @@
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
android:windowSoftInputMode="adjustResize"
android:screenOrientation="portrait"
>
<!-- Specifies an Android theme to apply to this Activity as soon as
the Android process has started. This theme is visible to the user
while the Flutter UI initializes. After that, this theme continues

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

View File

@@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<external-path name="external_storage_directory" path="." />
<external-path name="external_files" path="."/>
</resources>

View File

@@ -0,0 +1,7 @@
<network-security-config>
<base-config cleartextTrafficPermitted="true">
<trust-anchors>
<certificates src="system" />
</trust-anchors>
</base-config>
</network-security-config>

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -0,0 +1,30 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="32" height="32" fill="#F5F5F5"/>
<path d="M-2213 -1590C-2213 -1591.1 -2212.1 -1592 -2211 -1592H1326C1327.1 -1592 1328 -1591.1 1328 -1590V2084C1328 2085.1 1327.1 2086 1326 2086H-2211C-2212.1 2086 -2213 2085.1 -2213 2084V-1590Z" fill="#2C2C2D"/>
<path d="M-2211 -1591H1326V-1593H-2211V-1591ZM1327 -1590V2084H1329V-1590H1327ZM1326 2085H-2211V2087H1326V2085ZM-2212 2084V-1590H-2214V2084H-2212ZM-2211 2085C-2211.55 2085 -2212 2084.55 -2212 2084H-2214C-2214 2085.66 -2212.66 2087 -2211 2087V2085ZM1327 2084C1327 2084.55 1326.55 2085 1326 2085V2087C1327.66 2087 1329 2085.66 1329 2084H1327ZM1326 -1591C1326.55 -1591 1327 -1590.55 1327 -1590H1329C1329 -1591.66 1327.66 -1593 1326 -1593V-1591ZM-2211 -1593C-2212.66 -1593 -2214 -1591.66 -2214 -1590H-2212C-2212 -1590.55 -2211.55 -1591 -2211 -1591V-1593Z" fill="black" fill-opacity="0.1"/>
<rect width="390" height="1084" transform="translate(-44 -414)" fill="white"/>
<g filter="url(#filter0_dd_11_141)">
<rect x="-24" y="-28" width="350" height="145" rx="12" fill="white"/>
<rect x="-12" y="-12" width="56" height="56" rx="8" fill="#F15A29" fill-opacity="0.08"/>
<path d="M20 14.6666H20.0133M14.6666 20H14.68M21.3333 21.3333H21.3466M2.66663 21.3333L29.3333 29.3333L21.3333 2.66663C16.8694 3.89048 12.801 6.25514 9.52805 9.52805C6.25514 12.801 3.89048 16.8694 2.66663 21.3333Z" stroke="#F15A29" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M7.61328 22.8133C8.69531 19.2219 10.6497 15.9542 13.3019 13.3019C15.9542 10.6497 19.2219 8.69531 22.8133 7.61328" stroke="#F15A29" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<defs>
<filter id="filter0_dd_11_141" x="-44" y="-36" width="390" height="185" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feMorphology radius="4" operator="erode" in="SourceAlpha" result="effect1_dropShadow_11_141"/>
<feOffset dy="12"/>
<feGaussianBlur stdDeviation="12"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.568627 0 0 0 0 0.619608 0 0 0 0 0.670588 0 0 0 0.12 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_11_141"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset/>
<feGaussianBlur stdDeviation="1"/>
<feComposite in2="hardAlpha" operator="out"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.568627 0 0 0 0 0.619608 0 0 0 0 0.670588 0 0 0 0.2 0"/>
<feBlend mode="normal" in2="effect1_dropShadow_11_141" result="effect2_dropShadow_11_141"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect2_dropShadow_11_141" result="shape"/>
</filter>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

@@ -0,0 +1,5 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M28 16V9.33333H6.66667C5.95942 9.33333 5.28115 9.05238 4.78105 8.55229C4.28095 8.05219 4 7.37391 4 6.66667C4 5.95942 4.28095 5.28115 4.78105 4.78105C5.28115 4.28095 5.95942 4 6.66667 4H25.3333V9.33333" stroke="#F15A29" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M4 6.66675V25.3334C4 26.0407 4.28095 26.7189 4.78105 27.219C5.28115 27.7191 5.95942 28.0001 6.66667 28.0001H28V21.3334" stroke="#F15A29" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M24 16C23.2928 16 22.6145 16.281 22.1144 16.781C21.6143 17.2811 21.3334 17.9594 21.3334 18.6667C21.3334 19.3739 21.6143 20.0522 22.1144 20.5523C22.6145 21.0524 23.2928 21.3333 24 21.3333H29.3334V16H24Z" stroke="#F15A29" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 904 B

View File

@@ -0,0 +1,5 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M20.0001 2.66663H12.0001C11.2637 2.66663 10.6667 3.26358 10.6667 3.99996V6.66663C10.6667 7.40301 11.2637 7.99996 12.0001 7.99996H20.0001C20.7365 7.99996 21.3334 7.40301 21.3334 6.66663V3.99996C21.3334 3.26358 20.7365 2.66663 20.0001 2.66663Z" stroke="#F15A29" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M13.8667 16.8C14.3971 16.2696 15.1166 15.9716 15.8667 15.9716C16.6169 15.9716 17.3363 16.2696 17.8667 16.8C18.3971 17.3304 18.6951 18.0498 18.6951 18.8C18.6951 19.5501 18.3971 20.2696 17.8667 20.8L10.6667 28L5.33337 29.3333L6.66671 24L13.8667 16.8Z" stroke="#F15A29" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M21.3334 5.33325H24C24.7073 5.33325 25.3856 5.6142 25.8857 6.1143C26.3858 6.6144 26.6667 7.29267 26.6667 7.99992V26.6666C26.6667 27.3738 26.3858 28.0521 25.8857 28.5522C25.3856 29.0523 24.7073 29.3333 24 29.3333H16.6667M5.33337 17.9999V7.99992C5.33337 7.29267 5.61433 6.6144 6.11442 6.1143C6.61452 5.6142 7.2928 5.33325 8.00004 5.33325H10.6667" stroke="#F15A29" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 775 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 716 B

View File

@@ -0,0 +1,4 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M26.6666 18.6666H5.33329C3.86053 18.6666 2.66663 19.8605 2.66663 21.3333V26.6666C2.66663 28.1394 3.86053 29.3333 5.33329 29.3333H26.6666C28.1394 29.3333 29.3333 28.1394 29.3333 26.6666V21.3333C29.3333 19.8605 28.1394 18.6666 26.6666 18.6666Z" stroke="#F15A29" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M8.01333 24H8M13.3467 24H13.3333M20 13.3333V18.6666M23.7867 9.55997C23.2913 9.0641 22.7031 8.67072 22.0557 8.40232C21.4082 8.13392 20.7142 7.99578 20.0133 7.99578C19.3125 7.99578 18.6184 8.13392 17.971 8.40232C17.3235 8.67072 16.7353 9.0641 16.24 9.55997M27.5467 5.78664C25.5465 3.78776 22.8344 2.66492 20.0067 2.66492C17.1789 2.66492 14.4669 3.78776 12.4667 5.78664" stroke="#F15A29" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 898 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 599 B

View File

@@ -0,0 +1,3 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M2.66663 29.3333L6.66663 25.3333M9.99996 18L13.3333 14.6667M14 22L17.3333 18.6667M24 4L18.6666 9.33333H26.6666L21.3333 14.6667M8.39996 27.0667C8.69725 27.365 9.05051 27.6017 9.43947 27.7632C9.82844 27.9247 10.2455 28.0079 10.6666 28.0079C11.0878 28.0079 11.5048 27.9247 11.8938 27.7632C12.2827 27.6017 12.636 27.365 12.9333 27.0667L16 24L7.99996 16L4.93329 19.0667C4.63497 19.364 4.39826 19.7172 4.23675 20.1062C4.07524 20.4951 3.9921 20.9122 3.9921 21.3333C3.9921 21.7545 4.07524 22.1715 4.23675 22.5605C4.39826 22.9495 4.63497 23.3027 4.93329 23.6L8.39996 27.0667Z" stroke="#F15A29" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 763 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 407 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 167 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 295 B

After

Width:  |  Height:  |  Size: 880 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 406 B

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 450 B

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 282 B

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 462 B

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 704 B

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 406 B

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 586 B

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 862 B

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 862 B

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 762 B

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 17 KiB

View File

@@ -10,13 +10,21 @@ class Constant {
static double designWidthPhone = 390;
static String bearerName = "petty-cash";
static String version = "1.00";
// static String version = "1.00";
// NOTE VERSI HARUS SAMA DENGAN PUBSPEC.YAML
static String version = "1.2.0";
// static String baseUrl = "https://devregonline.pramita.co.id/one-api/xdoc/";
// tester devbandungraya
static String baseUrlDevoneReport = "devone.aplikasi.web.id";
static String baseUrlDevone =
"http://devone.aplikasi.web.id/one-api-pettycash/pettycash/";
"https://devone.aplikasi.web.id/one-api-pettycash/pettycash/";
// "http://devone.aplikasi.web.id/one-api-pettycash/pettycash/";
static String baseUrlFile =
"https://devone.aplikasi.web.id/pettycash-media/attachment/";
// static String baseUrl_appdoctor =
// "http://devbandungraya.aplikasi.web.id/one-api/app_doctor/";
@@ -260,6 +268,18 @@ class Constant {
// background upload file
static Color bgUploadFile = Color.fromRGBO(207, 207, 207, 0.20);
// background icon history
static Color bgIconHistory = Color.fromRGBO(241, 90, 41, 0.16);
// background chip confirmed
static Color bgChipConfirmed = Color(0xffe8f4f5);
static Color bgTextChipConfirmed = Color(0xff32827C);
// delete text
static Color bgTextDelete = Color(0xffFF4842);
static Color bgTextChipDelete = Color.fromRGBO(255, 72, 66, 0.12);
//typoGraphy
static TextStyle titleH1Login({required BuildContext context}) {
return TextStyle(
@@ -268,6 +288,13 @@ class Constant {
);
}
static TextStyle titleH6_700({required BuildContext context}) {
return TextStyle(
fontSize: Constant.getActualYPhone(context: context, y: 18),
fontWeight: FontWeight.w700,
);
}
static TextStyle titleH3_700({required BuildContext context}) {
return TextStyle(
fontSize: Constant.getActualYPhone(context: context, y: 24),
@@ -339,6 +366,13 @@ class Constant {
);
}
static TextStyle H4_700V3({required BuildContext context}) {
return TextStyle(
fontSize: Constant.getActualYPhone(context: context, y: 16),
fontWeight: FontWeight.w700,
);
}
static TextStyle titleH5({required BuildContext context}) {
return TextStyle(
fontSize: Constant.getActualYPhone(context: context, y: 18),

View File

@@ -1,7 +1,11 @@
import '../screen/change_company/change_company.dart';
import 'package:app_petty_cash/screen/camera/coba_camera.dart';
import 'package:app_petty_cash/screen/camera/example.dart';
import 'package:flutter/material.dart';
import '../screen/change_password/change_password_screen.dart';
import '../screen/home/home_screen.dart';
import '../screen/transaksi/history_transaksi_screen.dart';
import '../screen/transaksi/transaksi_screen.dart';
import '../screen/login/login_screen.dart';
import '../screen/splash/splash_screen.dart';
@@ -16,12 +20,32 @@ const homeRoute = "/homeRoute";
const transaksiRoute = "/transaksiRoute";
const userRoute = "/userRoute";
const reportRoute = "/reportRoute";
const changeCompanyRoute = "/changeCompanyRoute";
const historyTransaksiRoute = "/historyTransaksiRoute";
const cameraExampleRoute = "/cameraExampleRoute";
const cobaCameraRoute = "/cobaCameraRoute";
// test screen
const testFilePickerRoute = "/testFilePickerRoute";
const changePasswordRoute = "/changePasswordRoute";
class AppRoute {
static Route<dynamic> generateRoute(RouteSettings settings) {
// change password
if (settings.name == changePasswordRoute) {
return MaterialPageRoute(builder: (context) {
return MediaQuery(
data: MediaQuery.of(context).copyWith(
textScaleFactor: 1.0,
padding: EdgeInsets.all(0),
),
child: ChangePasswordScreen(),
);
});
}
// splash screen
if (settings.name == splashScreen) {
return MaterialPageRoute(builder: (context) {
@@ -35,6 +59,32 @@ class AppRoute {
});
}
// change Company
if (settings.name == changeCompanyRoute) {
return MaterialPageRoute(builder: (context) {
return MediaQuery(
data: MediaQuery.of(context).copyWith(
textScaleFactor: 1.0,
padding: EdgeInsets.all(0),
),
child: ChangeCompanyScreen(),
);
});
}
// history transaksi
if (settings.name == historyTransaksiRoute) {
return MaterialPageRoute(builder: (context) {
return MediaQuery(
data: MediaQuery.of(context).copyWith(
textScaleFactor: 1.0,
padding: EdgeInsets.all(0),
),
child: HistoryTransaksiScreen(),
);
});
}
// report screen
if (settings.name == reportRoute) {
return MaterialPageRoute(builder: (context) {
@@ -99,6 +149,30 @@ class AppRoute {
);
});
}
// Camera example route
if (settings.name == cameraExampleRoute) {
return MaterialPageRoute(builder: (context) {
return MediaQuery(
data: MediaQuery.of(context).copyWith(
textScaleFactor: 1.0,
padding: EdgeInsets.all(0),
),
child: CamerExample(),
);
});
}
// Coba Camera route
if (settings.name == cobaCameraRoute) {
return MaterialPageRoute(builder: (context) {
return MediaQuery(
data: MediaQuery.of(context).copyWith(
textScaleFactor: 1.0,
padding: EdgeInsets.all(0),
),
child: CobaCamera(),
);
});
}
// default
return MaterialPageRoute(builder: (context) {

View File

@@ -2,10 +2,15 @@ import 'package:flutter/material.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:intl/date_symbol_data_local.dart';
import 'app/route.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
initializeDateFormatting();
await SystemChrome.setPreferredOrientations([
DeviceOrientation.portraitUp,
]);
SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle(
statusBarIconBrightness:
Brightness.dark, // this will change the brightness of the icons
@@ -39,8 +44,8 @@ class MyApp extends StatelessWidget {
),
debugShowCheckedModeBanner: false,
// initialRoute: loginRoute,
// initialRoute: splashScreen,
initialRoute: transaksiRoute,
initialRoute: splashScreen,
// initialRoute: transaksiRoute,
// initialRoute: reportRoute,
// initialRoute: testFilePickerRoute,
onGenerateRoute: AppRoute.generateRoute,

View File

@@ -25,8 +25,8 @@ class AuthDoctorModel {
late String M_UserID;
late String M_UserUsername;
late String M_UserEmail;
// late String M_UserM_DoctorCode;
// late String M_UserM_DoctorID;
late String M_CompanyID;
late String M_CompanyName;
// late String M_UserM_StaffID;
// late String M_UserM_MouID;
late String ip;
@@ -39,8 +39,8 @@ class AuthDoctorModel {
required this.M_UserID,
required this.M_UserUsername,
required this.M_UserEmail,
// required this.M_UserM_DoctorCode,
// required this.M_UserM_DoctorID,
required this.M_CompanyID,
required this.M_CompanyName,
// this.M_UserM_StaffID = "",
// this.M_UserM_MouID = "",
this.ip = "",
@@ -51,8 +51,8 @@ class AuthDoctorModel {
M_UserID = json['M_UserID'].toString();
M_UserUsername = json['M_UserUsername'].toString();
M_UserEmail = json['M_UserEmail'].toString();
// M_UserM_DoctorCode = json['M_UserM_DoctorCode'].toString();
// M_UserM_DoctorID = json['M_UserM_DoctorID'].toString();
M_CompanyID = json['M_CompanyID'].toString();
M_CompanyName = json['M_CompanyName'].toString();
// M_UserM_StaffID = json['M_UserM_StaffID'].toString();
// M_UserM_MouID = json['M_UserM_MouID'].toString();
ip = json['ip'].toString();
@@ -64,8 +64,8 @@ class AuthDoctorModel {
data['M_UserID'] = M_UserID;
data['M_UserUsername'] = M_UserUsername;
data['M_UserEmail'] = M_UserEmail;
// data['M_UserM_DoctorCode'] = M_UserM_DoctorCode;
// data['M_UserM_DoctorID'] = M_UserM_DoctorID;
data['M_CompanyID'] = M_CompanyID;
data['M_CompanyName'] = M_CompanyName;
// data['M_UserM_StaffID'] = M_UserM_StaffID;
// data['M_UserM_MouID'] = M_UserM_MouID;
data['ip'] = ip;

View File

@@ -0,0 +1,21 @@
class CompanyModel {
String? companyid;
String? companyname;
String? mUserDefaultCompany;
CompanyModel({this.companyid, this.companyname, this.mUserDefaultCompany});
CompanyModel.fromJson(Map<String, dynamic> json) {
companyid = json['companyid'];
companyname = json['companyname'];
mUserDefaultCompany = json['M_UserDefaultCompany'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['companyid'] = this.companyid;
data['companyname'] = this.companyname;
data['M_UserDefaultCompany'] = this.mUserDefaultCompany;
return data;
}
}

View File

@@ -0,0 +1,17 @@
class HistoryTotalModel {
String? totalAll;
HistoryTotalModel({
this.totalAll,
});
HistoryTotalModel.fromJson(Map<String, dynamic> json) {
totalAll = json['total_all'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['total_all'] = this.totalAll;
return data;
}
}

View File

@@ -0,0 +1,69 @@
class HistoryTransaksiModel {
String? id;
String? tanggaltransaksi;
String? tipe;
String? kategoriid;
String? kategoriname;
String? note;
String? amount;
String? sender;
String? imgurl;
String? isconfirm;
String? tanggalconfirm;
String? usertransaksi;
String? userconfirm;
String? tanggalcreated;
HistoryTransaksiModel(
{this.id,
this.tanggaltransaksi,
this.tipe,
this.kategoriid,
this.kategoriname,
this.note,
this.amount,
this.sender,
this.imgurl,
this.isconfirm,
this.tanggalconfirm,
this.usertransaksi,
this.userconfirm,
this.tanggalcreated
});
HistoryTransaksiModel.fromJson(Map<String, dynamic> json) {
id = json['id'];
tanggaltransaksi = json['tanggaltransaksi'];
tipe = json['tipe'];
kategoriid = json['kategoriid'];
kategoriname = json['kategoriname'];
note = json['note'];
amount = json['amount'];
sender = json['sender'];
imgurl = json['imgurl'];
isconfirm = json['isconfirm'];
tanggalconfirm = json['tanggalconfirm'];
usertransaksi = json['usertransaksi'];
userconfirm = json['userconfirm'];
tanggalcreated = json['tanggalcreated'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['id'] = this.id;
data['tanggaltransaksi'] = this.tanggaltransaksi;
data['tipe'] = this.tipe;
data['kategoriid'] = this.kategoriid;
data['kategoriname'] = this.kategoriname;
data['note'] = this.note;
data['amount'] = this.amount;
data['sender'] = this.sender;
data['imgurl'] = this.imgurl;
data['isconfirm'] = this.isconfirm;
data['tanggalconfirm'] = this.tanggalconfirm;
data['usertransaksi'] = this.usertransaksi;
data['userconfirm'] = this.userconfirm;
data['tanggalcreated'] = this.tanggalcreated;
return data;
}
}

View File

@@ -0,0 +1,21 @@
class InfoAccountBalanceModel {
String? totalAll;
String? kredit;
String? debit;
InfoAccountBalanceModel({this.totalAll, this.kredit, this.debit});
InfoAccountBalanceModel.fromJson(Map<String, dynamic> json) {
totalAll = json['total_all'];
kredit = json['kredit'];
debit = json['debit'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['total_all'] = this.totalAll;
data['kredit'] = this.kredit;
data['debit'] = this.debit;
return data;
}
}

View File

@@ -0,0 +1,69 @@
class ListTransaksiHomeModel {
String? id;
String? tanggaltransaksi;
String? tipe;
String? kategoriid;
String? kategoriname;
String? note;
String? amount;
String? sender;
String? imgurl;
String? isconfirm;
String? tanggalconfirm;
String? usertransaksi;
String? userconfirm;
String? tanggalcreated;
ListTransaksiHomeModel({
this.id,
this.tanggaltransaksi,
this.tipe,
this.kategoriid,
this.kategoriname,
this.note,
this.amount,
this.sender,
this.imgurl,
this.isconfirm,
this.tanggalconfirm,
this.usertransaksi,
this.userconfirm,
this.tanggalcreated,
});
ListTransaksiHomeModel.fromJson(Map<String, dynamic> json) {
id = json['id'];
tanggaltransaksi = json['tanggaltransaksi'];
tipe = json['tipe'];
kategoriid = json['kategoriid'];
kategoriname = json['kategoriname'];
note = json['note'];
amount = json['amount'];
sender = json['sender'];
imgurl = json['imgurl'];
isconfirm = json['isconfirm'];
tanggalconfirm = json['tanggalconfirm'];
usertransaksi = json['usertransaksi'];
userconfirm = json['userconfirm'];
tanggalcreated = json['tanggalcreated'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['id'] = this.id;
data['tanggaltransaksi'] = this.tanggaltransaksi;
data['tipe'] = this.tipe;
data['kategoriid'] = this.kategoriid;
data['kategoriname'] = this.kategoriname;
data['note'] = this.note;
data['amount'] = this.amount;
data['sender'] = this.sender;
data['imgurl'] = this.imgurl;
data['isconfirm'] = this.isconfirm;
data['tanggalconfirm'] = this.tanggalconfirm;
data['usertransaksi'] = this.usertransaksi;
data['userconfirm'] = this.userconfirm;
data['tanggalcreated'] = this.tanggalcreated;
return data;
}
}

View File

@@ -0,0 +1,11 @@
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../model/info_account_balance.dart';
final currentInfoAccountBalanceProvider = StateProvider<InfoAccountBalanceModel>(
((ref) => InfoAccountBalanceModel(
debit: "0",
kredit: "0",
totalAll: "0",
)),
);

View File

@@ -0,0 +1,9 @@
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../model/history_total_model.dart';
final currentHistoryTotalProvider = StateProvider<HistoryTotalModel>(
((ref) => HistoryTotalModel(
totalAll: "0",
)),
);

View File

@@ -1,3 +1,7 @@
import 'dart:convert';
import 'package:shared_preferences/shared_preferences.dart';
import '../app/constant.dart';
import '../model/auth_model.dart';
import 'base_repository.dart';
@@ -19,6 +23,8 @@ class AuthRepository extends BaseRepository {
// "doctor_id": "2891",
// "password": "3"
};
print(param);
// final service = "${Constant.baseUrlDevone}xauth/login";
final service = "${Constant.baseUrlDevone}auth/login";
print('url login $service');
@@ -82,7 +88,35 @@ class AuthRepository extends BaseRepository {
// model: AuthDoctorModel.fromJson(resp["data"]["user"]),
// );
// return result;
if (resp["status"] == "OK") {
return resp['message'];
} else {
return resp['message'];
}
}
Future<String> changePassword({
required String userID,
required String token,
required String tokenx,
required String oldPassword,
required String newPassword,
required String confirmPassword,
}) async {
final param = {
"tokenx": tokenx,
"token": token,
"M_UserID": userID,
"old_password": oldPassword,
"new_password": newPassword,
"confirm_password": confirmPassword
};
final service = "${Constant.baseUrlDevone}auth/change_password";
final resp = await post(param: param, service: service, token: token);
if (resp["status"] == "OK") {
return resp['message'];
} else {

View File

@@ -2,11 +2,33 @@ import 'dart:convert';
import 'dart:io';
import 'package:dio/dio.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../app/constant.dart';
abstract class BaseRepository {
final Dio dio;
BaseRepository({required this.dio});
Future<String> getCompanyID() async {
final shared = await SharedPreferences.getInstance();
String M_CompanyID = "0";
if (shared != null) {
final bearerString = shared.get(Constant.bearerName).toString();
final xmodel = jsonDecode(bearerString);
if (xmodel != null) {
M_CompanyID = xmodel["model"]["M_CompanyID"];
}
}
if (M_CompanyID == "0") {
throw BaseRepositoryException(message: 'Invalid Company ID');
}
return M_CompanyID;
}
Future<Map<String, dynamic>> post({
required Map<String, dynamic> param,
required String service,

View File

@@ -0,0 +1,32 @@
import 'dart:convert';
import 'package:app_petty_cash/model/company_model.dart';
import 'package:app_petty_cash/model/list_type_model.dart';
import '../app/constant.dart';
import '../model/history_transaksi_model.dart';
import '../model/list_category_model.dart';
import 'base_repository.dart';
class CompanyRepository extends BaseRepository {
CompanyRepository({required super.dio});
// list company
Future<List<CompanyModel>> getListCompany({required String userID}) async {
// https: //devone.aplikasi.web.id/one-api-pettycash/pettycash/Usercompany/list_company?M_UserID=2
final service =
"${Constant.baseUrlDevone}Usercompany/list_company?M_UserID=${userID}";
final resp = await get(
service: service,
);
print("url list type : $service");
final result = List<CompanyModel>.empty(growable: true);
resp['data'].forEach((e) {
final model = CompanyModel.fromJson(e);
result.add(model);
});
return result;
}
}

View File

@@ -0,0 +1,61 @@
import 'package:app_petty_cash/model/list_type_model.dart';
import '../app/constant.dart';
import '../model/info_account_balance.dart';
import '../model/list_transaksi_model.dart';
import 'base_repository.dart';
class HomeRepository extends BaseRepository {
HomeRepository({required super.dio});
// list transaksi home
Future<List<ListTransaksiHomeModel>> getTransaksiListHome(
String companyid,
) async {
final service =
"${Constant.baseUrlDevone}homescreen/list_transaction/?companyid=$companyid";
final resp = await get(
// param: {
// "": "",
// },
service: service,
);
print("url list transaksi home : $service");
final result = List<ListTransaksiHomeModel>.empty(growable: true);
resp['data'].forEach((e) {
final model = ListTransaksiHomeModel.fromJson(e);
result.add(model);
});
return result;
}
// info account balance
Future<InfoAccountBalanceModel> getInfoAccountBalance(
String companyid,
) async {
final service =
"${Constant.baseUrlDevone}homescreen/list_total/?companyid=$companyid";
final resp = await get(
// param: {
// "": "",
// },
service: service,
);
print("url card info account balance home : $service");
InfoAccountBalanceModel result = InfoAccountBalanceModel(
debit: "0",
kredit: "0",
totalAll: "0",
);
print(resp['data']);
final model = InfoAccountBalanceModel.fromJson(resp['data']);
result = model;
return result;
}
}

View File

@@ -1,6 +1,10 @@
import 'dart:convert';
import 'package:app_petty_cash/model/list_type_model.dart';
import '../app/constant.dart';
import '../model/history_total_model.dart';
import '../model/history_transaksi_model.dart';
import '../model/list_category_model.dart';
import 'base_repository.dart';
@@ -56,31 +60,69 @@ class TransaksiRepository extends BaseRepository {
String catatan,
String userid,
String sender,
String base64file,
String fileName,
String fileSize,
String fileExtension,
String url) async {
String paramPostInUrl = "";
if (tipe == "KREDIT") {
// String paramPostInUrl = "";
Map<String, dynamic> prm = {};
String M_CompanyID = await getCompanyID();
if (tipe == "DEBIT") {
sender = "";
paramPostInUrl =
"?tanggal=$tanggal&tipe=$tipe&kategoriid=$kategoriid&jumlah=$jumlah&catatan=$catatan&url=$url&userid=$userid&sender=$sender";
prm = {
'tanggal': tanggal,
'tipe': tipe,
'kategoriid': kategoriid,
'jumlah': jumlah,
'catatan': catatan,
'url': url,
'userid': userid,
'sender': sender,
'companyid': M_CompanyID,
'base64File': base64file,
'fileName': fileName,
'fileSize': fileSize,
'fileEkstension': fileExtension
};
// paramPostInUrl =
// "?tanggal=$tanggal&tipe=$tipe&kategoriid=$kategoriid&jumlah=$jumlah&catatan=$catatan&url=$url&userid=$userid&sender=$sender";
} else {
if (tipe == "DEBIT") {
if (tipe == "KREDIT") {
kategoriid = "0";
paramPostInUrl =
"?tanggal=$tanggal&tipe=$tipe&kategoriid=$kategoriid&jumlah=$jumlah&catatan=$catatan&url=$url&userid=$userid&sender=$sender";
prm = {
'tanggal': tanggal,
'tipe': tipe,
'kategoriid': kategoriid,
'jumlah': jumlah,
'catatan': catatan,
'url': url,
'userid': userid,
'sender': sender,
'companyid': M_CompanyID,
'base64File': base64file,
'fileName': fileName,
'fileSize': fileSize,
'fileEkstension': fileExtension
};
// paramPostInUrl =
// "?tanggal=$tanggal&tipe=$tipe&kategoriid=$kategoriid&jumlah=$jumlah&catatan=$catatan&url=$url&userid=$userid&sender=$sender";
}
}
// paramPostInUrl += "&companyid=$M_CompanyID";
// /?tanggal=2023-12-29&tipe=KREDIT&kategoriid=3&jumlah=5000&catatan=Lakban%20Besar&url=&userid=1&sender=
final service =
"${Constant.baseUrlDevone}transaction/addtransaction/$paramPostInUrl";
final resp = await get(
// param: {
// "": "",
// },
service: service,
);
final service = "${Constant.baseUrlDevone}transaction/addtransaction";
final resp = await post(
// param: {
// "": "",
// },
service: service,
param: prm);
print("url insert transaksi : $service");
print("prm insert transaksi : ${jsonEncode(prm)}");
// final result = List<ListCategory>.empty(growable: true);
// resp['data'].forEach((e) {
@@ -90,4 +132,118 @@ class TransaksiRepository extends BaseRepository {
return resp['status'];
}
// list history transaksi
Future<List<HistoryTransaksiModel>> getListHistoryTransaksi(
String companyid,
String tglAwal,
String tglAkhir,
String categoryid,
) async {
final service =
"${Constant.baseUrlDevone}/history/list_transaction/?companyid=$companyid&startdate=$tglAwal&enddate=$tglAkhir&kategoriid=$categoryid";
// https://devone.aplikasi.web.id/one-api-pettycash/pettycash/history/list_transaction/?companyid=1&startdate=2023-12-01&enddate=2023-12-30&kategoriid=0
final resp = await get(
// param: {
// "": "",
// },
service: service,
);
print("url list history transaksi : $service");
final result = List<HistoryTransaksiModel>.empty(growable: true);
resp['data'].forEach((e) {
final model = HistoryTransaksiModel.fromJson(e);
result.add(model);
});
return result;
}
// delete transaksi
Future<String> deleteTransaksi(
String id,
String userid,
) async {
final service =
"${Constant.baseUrlDevone}transaction/deletetransaction/?id=$id&userid=$userid";
final resp = await get(
// param: {
// "": "",
// },
service: service,
);
print("url delete transaksi : $service");
// final result = List<ListCategory>.empty(growable: true);
// resp['data'].forEach((e) {
// final model = ListCategory.fromJson(e);
// result.add(model);
// });
if (resp['status'] != "OK") {
return resp['message'];
}
return resp['status'];
}
// confirm transaksi
Future<String> confirmTransaksi(
String id,
String userid,
) async {
final service =
"${Constant.baseUrlDevone}transaction/confirmtransaction/?id=$id&userid=$userid";
final resp = await get(
// param: {
// "": "",
// },
service: service,
);
print("url confirm transaksi : $service");
// final result = List<ListCategory>.empty(growable: true);
// resp['data'].forEach((e) {
// final model = ListCategory.fromJson(e);
// result.add(model);
// });
if (resp['status'] != "OK") {
return resp['message'];
}
return resp['status'];
}
// total by category di history
Future<HistoryTotalModel> getHistoryTotal(
String companyid,
String tglAwal,
String tglAkhir,
String categoryid,
) async {
final service =
"${Constant.baseUrlDevone}history/list_total/?companyid=$companyid&startdate=$tglAwal&enddate=$tglAkhir&kategoriid=$categoryid";
final resp = await get(
// param: {
// "": "",
// },
service: service,
);
print("url total History Transaksi : $service");
HistoryTotalModel result = HistoryTotalModel(
totalAll: "0",
);
// print(resp['data']);
final model = HistoryTotalModel.fromJson(resp['data']);
result = model;
return result;
}
}

View File

@@ -0,0 +1,128 @@
import 'dart:convert';
import 'dart:io';
import 'dart:io' as io;
import 'package:app_petty_cash/app/constant.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:image_picker/image_picker.dart';
import 'package:mime/mime.dart';
class CobaCamera extends HookConsumerWidget {
const CobaCamera({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final photo = useState<XFile?>(null);
final photoBase64 = useState<String>("");
final previewLoading = useState(false);
getBase64() async {
// List<int> imageBytes = await photo.value?.readAsBytes();
if (photo.value != null) {
final bytes = io.File(photo.value!.path).readAsBytesSync();
String base64Image = base64Encode(bytes);
photoBase64.value = base64Image;
final file = File(photo.value!.path);
print(file.lengthSync() / 1000000);
print(await photo.value!.length());
// await getExternalStorageDirectory();
// print(await photo.value!.saveTo(path));
print(base64Image);
}
}
final ImagePicker _picker = ImagePicker();
pickImage() async {
previewLoading.value = true;
final XFile? pickedFile = await _picker.pickImage(
source: ImageSource.camera,
);
photo.value = pickedFile;
// final Directory appDocumentsDir =
// await getApplicationDocumentsDirectory();
// print(appDocumentsDir);
// await pickedFile!.saveTo(appDocumentsDir.path);
await getBase64();
previewLoading.value = false;
}
browseImage() async {
previewLoading.value = true;
final XFile? pickedFile = await _picker.pickImage(
source: ImageSource.gallery,
);
photo.value = pickedFile;
getBase64();
previewLoading.value = false;
}
return Scaffold(
body: SingleChildScrollView(
child: SafeArea(
minimum: const EdgeInsets.all(20),
child: Column(
children: [
Container(
height: Constant.getActualYPhone(context: context, y: 500),
width: MediaQuery.of(context).size.width,
color: Colors.red,
child: LayoutBuilder(
builder: (context, constraints) {
final String? mime =
lookupMimeType(photo.value?.path ?? "");
return Semantics(
label: 'image_picker_example_picked_image',
child: (mime != null
? (mime.startsWith('image/'))
? Image.file(
// image: AssetImage(photo.value!.path),
File(photo.value!.path),
frameBuilder: (context, child, frame,
wasSynchronouslyLoaded) {
return (wasSynchronouslyLoaded)
? Center(
child: Text("Loadinga"),
)
: Container(
child: child,
);
},
errorBuilder: (BuildContext context,
Object error, StackTrace? stackTrace) {
return const Center(
child: Text(
'This image type is not supported'));
},
)
: null
: null));
},
),
),
// photo.value != null
// ? PhotoView(imageProvider: AssetImage(photo.value!.path))
// : Container(),
Row(
children: [
ElevatedButton(
onPressed: () {
pickImage();
},
child: Text("Take a picture")),
ElevatedButton(
onPressed: () {
browseImage();
},
child: Text("Browse a picture"))
],
),
],
),
),
),
);
}
}

View File

@@ -0,0 +1,542 @@
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:mime/mime.dart';
import 'package:video_player/video_player.dart';
class CamerExample extends StatefulWidget {
const CamerExample({super.key});
@override
State<CamerExample> createState() => _CamerExampleState();
}
class _CamerExampleState extends State<CamerExample> {
List<XFile>? _mediaFileList;
void _setImageFileListFromFile(XFile? value) {
_mediaFileList = value == null ? null : <XFile>[value];
}
dynamic _pickImageError;
bool isVideo = false;
VideoPlayerController? _controller;
VideoPlayerController? _toBeDisposed;
String? _retrieveDataError;
final ImagePicker _picker = ImagePicker();
final TextEditingController maxWidthController = TextEditingController();
final TextEditingController maxHeightController = TextEditingController();
final TextEditingController qualityController = TextEditingController();
Future<void> _playVideo(XFile? file) async {
if (file != null && mounted) {
await _disposeVideoController();
late VideoPlayerController controller;
if (kIsWeb) {
controller = VideoPlayerController.networkUrl(Uri.parse(file.path));
} else {
controller = VideoPlayerController.file(File(file.path));
}
_controller = controller;
// In web, most browsers won't honor a programmatic call to .play
// if the video has a sound track (and is not muted).
// Mute the video so it auto-plays in web!
// This is not needed if the call to .play is the result of user
// interaction (clicking on a "play" button, for example).
const double volume = kIsWeb ? 0.0 : 1.0;
await controller.setVolume(volume);
await controller.initialize();
await controller.setLooping(true);
await controller.play();
setState(() {});
}
}
Future<void> _onImageButtonPressed(
ImageSource source, {
required BuildContext context,
bool isMultiImage = false,
bool isMedia = false,
}) async {
if (_controller != null) {
await _controller!.setVolume(0.0);
}
if (context.mounted) {
if (isVideo) {
final XFile? file = await _picker.pickVideo(
source: source, maxDuration: const Duration(seconds: 10));
await _playVideo(file);
} else if (isMultiImage) {
await _displayPickImageDialog(context,
(double? maxWidth, double? maxHeight, int? quality) async {
try {
final List<XFile> pickedFileList = isMedia
? await _picker.pickMultipleMedia(
maxWidth: maxWidth,
maxHeight: maxHeight,
imageQuality: quality,
)
: await _picker.pickMultiImage(
maxWidth: maxWidth,
maxHeight: maxHeight,
imageQuality: quality,
);
setState(() {
_mediaFileList = pickedFileList;
});
} catch (e) {
setState(() {
_pickImageError = e;
});
}
});
} else if (isMedia) {
await _displayPickImageDialog(context,
(double? maxWidth, double? maxHeight, int? quality) async {
try {
final List<XFile> pickedFileList = <XFile>[];
final XFile? media = await _picker.pickMedia(
maxWidth: maxWidth,
maxHeight: maxHeight,
imageQuality: quality,
);
if (media != null) {
pickedFileList.add(media);
setState(() {
_mediaFileList = pickedFileList;
});
}
} catch (e) {
setState(() {
_pickImageError = e;
});
}
});
} else {
await _displayPickImageDialog(context,
(double? maxWidth, double? maxHeight, int? quality) async {
try {
final XFile? pickedFile = await _picker.pickImage(
source: source,
maxWidth: maxWidth,
maxHeight: maxHeight,
imageQuality: quality,
);
setState(() {
_setImageFileListFromFile(pickedFile);
});
} catch (e) {
setState(() {
_pickImageError = e;
});
}
});
}
}
}
@override
void deactivate() {
if (_controller != null) {
_controller!.setVolume(0.0);
_controller!.pause();
}
super.deactivate();
}
@override
void dispose() {
_disposeVideoController();
maxWidthController.dispose();
maxHeightController.dispose();
qualityController.dispose();
super.dispose();
}
Future<void> _disposeVideoController() async {
if (_toBeDisposed != null) {
await _toBeDisposed!.dispose();
}
_toBeDisposed = _controller;
_controller = null;
}
Widget _previewVideo() {
final Text? retrieveError = _getRetrieveErrorWidget();
if (retrieveError != null) {
return retrieveError;
}
if (_controller == null) {
return const Text(
'You have not yet picked a video',
textAlign: TextAlign.center,
);
}
return Padding(
padding: const EdgeInsets.all(10.0),
child: AspectRatioVideo(_controller),
);
}
Widget _previewImages() {
final Text? retrieveError = _getRetrieveErrorWidget();
if (retrieveError != null) {
return retrieveError;
}
if (_mediaFileList != null) {
return Semantics(
label: 'image_picker_example_picked_images',
child: ListView.builder(
key: UniqueKey(),
itemBuilder: (BuildContext context, int index) {
final String? mime = lookupMimeType(_mediaFileList![index].path);
// Why network for web?
// See https://pub.dev/packages/image_picker_for_web#limitations-on-the-web-platform
return Semantics(
label: 'image_picker_example_picked_image',
child: kIsWeb
? Image.network(_mediaFileList![index].path)
: (mime == null || mime.startsWith('image/')
? Image.file(
File(_mediaFileList![index].path),
errorBuilder: (BuildContext context, Object error,
StackTrace? stackTrace) {
return const Center(
child:
Text('This image type is not supported'));
},
)
: _buildInlineVideoPlayer(index)),
);
},
itemCount: _mediaFileList!.length,
),
);
} else if (_pickImageError != null) {
return Text(
'Pick image error: $_pickImageError',
textAlign: TextAlign.center,
);
} else {
return const Text(
'You have not yet picked an image.',
textAlign: TextAlign.center,
);
}
}
Widget _buildInlineVideoPlayer(int index) {
final VideoPlayerController controller =
VideoPlayerController.file(File(_mediaFileList![index].path));
const double volume = kIsWeb ? 0.0 : 1.0;
controller.setVolume(volume);
controller.initialize();
controller.setLooping(true);
controller.play();
return Center(child: AspectRatioVideo(controller));
}
Widget _handlePreview() {
if (isVideo) {
return _previewVideo();
} else {
return _previewImages();
}
}
Future<void> retrieveLostData() async {
final LostDataResponse response = await _picker.retrieveLostData();
if (response.isEmpty) {
return;
}
if (response.file != null) {
if (response.type == RetrieveType.video) {
isVideo = true;
await _playVideo(response.file);
} else {
isVideo = false;
setState(() {
if (response.files == null) {
_setImageFileListFromFile(response.file);
} else {
_mediaFileList = response.files;
}
});
}
} else {
_retrieveDataError = response.exception!.code;
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Camera example"),
),
body: Center(
child: !kIsWeb && defaultTargetPlatform == TargetPlatform.android
? FutureBuilder<void>(
future: retrieveLostData(),
builder: (BuildContext context, AsyncSnapshot<void> snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
case ConnectionState.waiting:
return const Text(
'You have not yet picked an image.',
textAlign: TextAlign.center,
);
case ConnectionState.done:
return _handlePreview();
case ConnectionState.active:
if (snapshot.hasError) {
return Text(
'Pick image/video error: ${snapshot.error}}',
textAlign: TextAlign.center,
);
} else {
return const Text(
'You have not yet picked an image.',
textAlign: TextAlign.center,
);
}
}
},
)
: _handlePreview(),
),
floatingActionButton: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
Semantics(
label: 'image_picker_example_from_gallery',
child: FloatingActionButton(
onPressed: () {
isVideo = false;
_onImageButtonPressed(ImageSource.gallery, context: context);
},
heroTag: 'image0',
tooltip: 'Pick Image from gallery',
child: const Icon(Icons.photo),
),
),
Padding(
padding: const EdgeInsets.only(top: 16.0),
child: FloatingActionButton(
onPressed: () {
isVideo = false;
_onImageButtonPressed(
ImageSource.gallery,
context: context,
isMultiImage: true,
isMedia: true,
);
},
heroTag: 'multipleMedia',
tooltip: 'Pick Multiple Media from gallery',
child: const Icon(Icons.photo_library),
),
),
Padding(
padding: const EdgeInsets.only(top: 16.0),
child: FloatingActionButton(
onPressed: () {
isVideo = false;
_onImageButtonPressed(
ImageSource.gallery,
context: context,
isMedia: true,
);
},
heroTag: 'media',
tooltip: 'Pick Single Media from gallery',
child: const Icon(Icons.photo_library),
),
),
Padding(
padding: const EdgeInsets.only(top: 16.0),
child: FloatingActionButton(
onPressed: () {
isVideo = false;
_onImageButtonPressed(
ImageSource.gallery,
context: context,
isMultiImage: true,
);
},
heroTag: 'image1',
tooltip: 'Pick Multiple Image from gallery',
child: const Icon(Icons.photo_library),
),
),
if (_picker.supportsImageSource(ImageSource.camera))
Padding(
padding: const EdgeInsets.only(top: 16.0),
child: FloatingActionButton(
onPressed: () {
isVideo = false;
_onImageButtonPressed(ImageSource.camera, context: context);
},
heroTag: 'image2',
tooltip: 'Take a Photo',
child: const Icon(Icons.camera_alt),
),
),
Padding(
padding: const EdgeInsets.only(top: 16.0),
child: FloatingActionButton(
backgroundColor: Colors.red,
onPressed: () {
isVideo = true;
_onImageButtonPressed(ImageSource.gallery, context: context);
},
heroTag: 'video0',
tooltip: 'Pick Video from gallery',
child: const Icon(Icons.video_library),
),
),
if (_picker.supportsImageSource(ImageSource.camera))
Padding(
padding: const EdgeInsets.only(top: 16.0),
child: FloatingActionButton(
backgroundColor: Colors.red,
onPressed: () {
isVideo = true;
_onImageButtonPressed(ImageSource.camera, context: context);
},
heroTag: 'video1',
tooltip: 'Take a Video',
child: const Icon(Icons.videocam),
),
),
],
),
);
}
Text? _getRetrieveErrorWidget() {
if (_retrieveDataError != null) {
final Text result = Text(_retrieveDataError!);
_retrieveDataError = null;
return result;
}
return null;
}
Future<void> _displayPickImageDialog(
BuildContext context, OnPickImageCallback onPick) async {
return showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: const Text('Add optional parameters'),
content: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
TextField(
controller: maxWidthController,
keyboardType:
const TextInputType.numberWithOptions(decimal: true),
decoration: const InputDecoration(
hintText: 'Enter maxWidth if desired'),
),
TextField(
controller: maxHeightController,
keyboardType:
const TextInputType.numberWithOptions(decimal: true),
decoration: const InputDecoration(
hintText: 'Enter maxHeight if desired'),
),
TextField(
controller: qualityController,
keyboardType: TextInputType.number,
decoration: const InputDecoration(
hintText: 'Enter quality if desired'),
),
],
),
actions: <Widget>[
TextButton(
child: const Text('CANCEL'),
onPressed: () {
Navigator.of(context).pop();
},
),
TextButton(
child: const Text('PICK'),
onPressed: () {
final double? width = maxWidthController.text.isNotEmpty
? double.parse(maxWidthController.text)
: null;
final double? height = maxHeightController.text.isNotEmpty
? double.parse(maxHeightController.text)
: null;
final int? quality = qualityController.text.isNotEmpty
? int.parse(qualityController.text)
: null;
onPick(width, height, quality);
Navigator.of(context).pop();
}),
],
);
});
}
}
typedef OnPickImageCallback = void Function(
double? maxWidth, double? maxHeight, int? quality);
class AspectRatioVideo extends StatefulWidget {
const AspectRatioVideo(this.controller, {super.key});
final VideoPlayerController? controller;
@override
AspectRatioVideoState createState() => AspectRatioVideoState();
}
class AspectRatioVideoState extends State<AspectRatioVideo> {
VideoPlayerController? get controller => widget.controller;
bool initialized = false;
void _onVideoControllerUpdate() {
if (!mounted) {
return;
}
if (initialized != controller!.value.isInitialized) {
initialized = controller!.value.isInitialized;
setState(() {});
}
}
@override
void initState() {
super.initState();
controller!.addListener(_onVideoControllerUpdate);
}
@override
void dispose() {
controller!.removeListener(_onVideoControllerUpdate);
super.dispose();
}
@override
Widget build(BuildContext context) {
if (initialized) {
return Center(
child: AspectRatio(
aspectRatio: controller!.value.aspectRatio,
child: VideoPlayer(controller!),
),
);
} else {
return Container();
}
}
}

View File

@@ -0,0 +1,209 @@
import 'dart:convert';
import 'package:app_petty_cash/model/auth_model.dart';
import 'package:app_petty_cash/model/company_model.dart';
import 'package:app_petty_cash/screen/change_company/list_company_provider.dart';
import 'package:app_petty_cash/widget/sankbar_widget.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../../app/constant.dart';
import '../../app/route.dart';
import '../../provider/current_user_provider.dart';
import '../../widget/custom_drawer.dart';
class ChangeCompanyScreen extends HookConsumerWidget {
const ChangeCompanyScreen({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final listCompany = useState<List<CompanyModel>>([
CompanyModel(companyid: "1", companyname: "SAS"),
CompanyModel(companyid: "2", companyname: "SIM")
]);
final isLoading = useState(false);
final selectedCompanyID = useState("0");
final selectedCompanyName = useState("");
final auth = ref.watch(currentUserProvider);
useEffect(() {
WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
final userID = ref.read(currentUserProvider)?.model.M_UserID ?? "0";
if (userID == "0") {
//not login
Navigator.of(context)
.pushNamedAndRemoveUntil(loginRoute, (route) => true);
// Navigator.popAndPushNamed(context, loginRoute);
return;
}
ref.read(ListCompanyProvider.notifier).getListCompany(userID: userID);
});
return () {};
}, []);
ref.listen(
ListCompanyProvider,
(previous, next) {
if (next is ListCompanyStateLoading) {
isLoading.value = true;
} else if (next is ListCompanyStateError) {
// print(next.message);
isLoading.value = false;
SanckbarWidget(context, next.message, snackbarType.error);
} else if (next is ListCompanyStateDone) {
print(jsonEncode(next.model));
// print(next.model.length);
listCompany.value = next.model;
selectedCompanyID.value = auth?.model.M_CompanyID ?? "0";
isLoading.value = false;
}
},
);
return Padding(
padding: EdgeInsets.only(
top: Constant.getActualYPhone(context: context, y: 30),
),
child: Scaffold(
appBar: AppBar(
// centerTitle: true,
title: Text(
'Change Company',
style: TextStyle(color: Constant.textWhite),
),
backgroundColor: Constant.pcBtnBackgroundColor,
iconTheme: IconThemeData(
color: Constant.textWhite,
),
),
drawer: CustomDrawer(),
body: SingleChildScrollView(
child: Padding(
padding: EdgeInsets.all(20),
child: isLoading.value
? Container(
height: MediaQuery.of(context).size.height,
child: Center(
child: CircularProgressIndicator(),
),
)
: Column(
children: listCompany.value
.map((e) => Container(
margin: EdgeInsets.only(bottom: 20),
child: InkWell(
onTap: () async {
selectedCompanyID.value = e.companyid ?? "0";
selectedCompanyName.value =
e.companyname ?? "0";
final shared =
await SharedPreferences.getInstance();
final oldData = jsonDecode(
shared.getString(Constant.bearerName) ??
"");
print(oldData['token']);
final newModel = AuthDoctorModel.fromJson(
oldData['model']);
newModel.M_CompanyID = e.companyid ?? "0";
newModel.M_CompanyName = e.companyname ?? "";
final token = jsonEncode({
"date": oldData['date'],
"model": newModel,
"token": oldData['token']
});
final newAuthData = AuthModel(
token: oldData['token'], model: newModel);
await shared.setString(
Constant.bearerName, token);
ref.read(currentUserProvider.notifier).state =
newAuthData;
Navigator.pushNamed(context, homeRoute);
},
child: Card(
shadowColor:
selectedCompanyID.value == e.companyid
? Constant.bgIconHistory
: Colors.white,
surfaceTintColor:
selectedCompanyID.value == e.companyid
? Constant.bgIconHistory
: Colors.white,
// elevation: selectedCompanyID.value == e.companyid
// ? 0
// : 1,
elevation: 10,
color: selectedCompanyID.value == e.companyid
? Constant.bgIconHistory
: Colors.white,
child: Container(
padding: EdgeInsets.all(20),
width: MediaQuery.of(context).size.width,
child: Row(
children: [
Expanded(
flex: 1,
child: Icon(
Icons.corporate_fare,
color:
selectedCompanyID.value ==
e.companyid
? Constant.textRed
: Colors.black,
size: 30,
)),
SizedBox(
width: 10,
),
Expanded(
flex: 8,
child: Text(e.companyname ?? "",
softWrap: true,
maxLines: 3,
// overflow: ,
style: Constant.body1(
context: context)
.copyWith(
color: selectedCompanyID
.value ==
e.companyid
? Constant.textRed
: Colors.black,
fontSize: 25)),
),
],
)),
),
),
))
.toList()
// [
// Card(
// shadowColor: Colors.white,
// // surfaceTintColor: Colors.white,
// elevation: 0,
// color: Constant.bgIconHistory,
// child: Container(
// padding: EdgeInsets.all(10),
// width: MediaQuery.of(context).size.width,
// child: Row(
// children: [
// Icon(Icons.corporate_fare),
// SizedBox(
// width: 10,
// ),
// Text("Company",
// style: Constant.body1(context: context)
// .copyWith(color: Constant.textRed)),
// ],
// )),
// )
// ],
),
),
),
),
);
}
}

View File

@@ -0,0 +1,69 @@
import 'package:app_petty_cash/model/company_model.dart';
import 'package:app_petty_cash/repository/company_repository.dart';
import '../../model/list_category_model.dart';
import '../../repository/transaksi_repository.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../provider/dio_provider.dart';
import '../../repository/base_repository.dart';
abstract class ListCompanyState extends Equatable {
final DateTime date;
const ListCompanyState(this.date);
@override
List<Object?> get props => [date];
}
class ListCompanyStateInit extends ListCompanyState {
ListCompanyStateInit() : super(DateTime.now());
}
class ListCompanyStateLoading extends ListCompanyState {
ListCompanyStateLoading() : super(DateTime.now());
}
class ListCompanyStateError extends ListCompanyState {
final String message;
ListCompanyStateError({
required this.message,
}) : super(DateTime.now());
}
class ListCompanyStateDone extends ListCompanyState {
final List<CompanyModel> model;
ListCompanyStateDone({
required this.model,
}) : super(DateTime.now());
}
//notifier
class ListCompanyNotifier extends StateNotifier<ListCompanyState> {
final Ref ref;
ListCompanyNotifier({
required this.ref,
}) : super(ListCompanyStateInit());
void getListCompany({required String userID}) async {
try {
state = ListCompanyStateLoading();
final dio = ref.read(dioProvider);
final resp =
await CompanyRepository(dio: dio).getListCompany(userID: userID);
state = ListCompanyStateDone(model: resp);
} catch (e) {
if (e is BaseRepositoryException) {
state = ListCompanyStateError(message: e.message.toString());
} else {
state = ListCompanyStateError(message: e.toString());
}
}
}
}
//provider
final ListCompanyProvider =
StateNotifierProvider<ListCompanyNotifier, ListCompanyState>(
(ref) => ListCompanyNotifier(ref: ref),
);

View File

@@ -0,0 +1,83 @@
import 'package:app_petty_cash/model/auth_model.dart';
import 'package:app_petty_cash/model/company_model.dart';
import 'package:app_petty_cash/repository/auth_repository.dart';
import 'package:app_petty_cash/repository/company_repository.dart';
import '../../model/list_category_model.dart';
import '../../repository/transaksi_repository.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../provider/dio_provider.dart';
import '../../repository/base_repository.dart';
abstract class ChangePasswordState extends Equatable {
final DateTime date;
const ChangePasswordState(this.date);
@override
List<Object?> get props => [date];
}
class ChangePasswordStateInit extends ChangePasswordState {
ChangePasswordStateInit() : super(DateTime.now());
}
class ChangePasswordStateLoading extends ChangePasswordState {
ChangePasswordStateLoading() : super(DateTime.now());
}
class ChangePasswordStateError extends ChangePasswordState {
final String message;
ChangePasswordStateError({
required this.message,
}) : super(DateTime.now());
}
class ChangePasswordStateDone extends ChangePasswordState {
final String model;
ChangePasswordStateDone({
required this.model,
}) : super(DateTime.now());
}
//notifier
class ChangePasswordNotifier extends StateNotifier<ChangePasswordState> {
final Ref ref;
ChangePasswordNotifier({
required this.ref,
}) : super(ChangePasswordStateInit());
void postChangePassword({
required String userID,
required String userToken,
required String userTokenx,
required String oldPassword,
required String newPassword,
required String confirmPassword,
}) async {
try {
state = ChangePasswordStateLoading();
final dio = ref.read(dioProvider);
final resp = await AuthRepository(dio: dio).changePassword(
userID: userID,
tokenx: userTokenx,
token: userToken,
oldPassword: oldPassword,
newPassword: newPassword,
confirmPassword: confirmPassword);
state = ChangePasswordStateDone(model: resp);
} catch (e) {
if (e is BaseRepositoryException) {
state = ChangePasswordStateError(message: e.message.toString());
} else {
state = ChangePasswordStateError(message: e.toString());
}
}
}
}
//provider
final ChangePasswordProvider =
StateNotifierProvider<ChangePasswordNotifier, ChangePasswordState>(
(ref) => ChangePasswordNotifier(ref: ref),
);

View File

@@ -0,0 +1,428 @@
import 'package:app_petty_cash/app/constant.dart';
import 'package:app_petty_cash/widget/custom_drawer.dart';
import 'package:app_petty_cash/widget/sankbar_widget.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../../app/route.dart';
import '../../provider/current_user_provider.dart';
import 'change_password_provider.dart';
class ChangePasswordScreen extends HookConsumerWidget {
const ChangePasswordScreen({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final selectedUser = ref.read(currentUserProvider);
//menampung user token jika ada
final userToken = ref.read(currentUserProvider)?.token ?? "";
final userID = ref.read(currentUserProvider)?.model.M_UserID ?? "0";
final tokenx = ref.read(currentUserProvider)?.token ?? "";
final isLoading = useState(false);
final obscureTextOldPassword = useState<bool>(true);
final obscureTextNewPassword = useState<bool>(true);
final obscureTextConfirmPassword = useState<bool>(true);
final ctrlOldPassword = useTextEditingController(text: "");
final ctrlNewPassword = useTextEditingController(text: "");
final ctrlConfirmPassword = useTextEditingController(text: "");
final loading = useState(false);
// inisialisasi agar bisa cek apakah user sudah login sukses apa belum
useEffect(() {
WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
final userID = ref.read(currentUserProvider)?.model.M_UserID ?? "0";
if (userID == "0") {
//not login
Navigator.of(context)
.pushNamedAndRemoveUntil(loginRoute, (route) => true);
// Navigator.popAndPushNamed(context, loginRoute);
return;
}
});
return () {};
}, []);
// fungsi logout
Logout() async {
final shared = await SharedPreferences.getInstance();
final bearerString = shared.get(Constant.bearerName).toString();
// print(bearerString);
if (bearerString.isNotEmpty) {
shared.remove(bearerString);
shared.clear();
// Navigator.popAndPushNamed(context, loginRoute);
ref.read(currentUserProvider.notifier).state = null;
Navigator.of(context)
.pushNamedAndRemoveUntil(loginRoute, (route) => false);
}
}
// read provider home
ref.listen(
ChangePasswordProvider,
(previous, next) {
if (next is ChangePasswordStateLoading) {
loading.value = true;
} else if (next is ChangePasswordStateError) {
loading.value = false;
SanckbarWidget(context, next.message, snackbarType.error);
} else if (next is ChangePasswordStateDone) {
loading.value = false;
SanckbarWidget(
context,
"Ubah Password Berhasil!\nSilahkan Login dengan password baru",
snackbarType.success,
);
Logout();
}
},
);
return Padding(
padding: EdgeInsets.only(
top: Constant.getActualYPhone(context: context, y: 30)),
child: Scaffold(
appBar: AppBar(
// centerTitle: true,
title: Text(
'Change Password',
style: TextStyle(color: Constant.textWhite),
),
backgroundColor: Constant.pcBtnBackgroundColor,
iconTheme: IconThemeData(
color: Constant.textWhite,
),
),
drawer: CustomDrawer(),
body: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(20),
child: Container(
width: Constant.getActualXPhone(context: context, x: 390),
height: Constant.getActualYPhone(context: context, y: 844),
child: Column(
children: [
//Old password
Padding(
padding: EdgeInsets.only(
// top: Constant.getActualYPhone(context: context, y: 63),
left: Constant.getActualXPhone(context: context, x: 20),
right: Constant.getActualXPhone(context: context, x: 20),
),
child: Align(
alignment: Alignment.centerLeft,
child: Text(
'Old Password',
style: Constant.body1_400_dibulan(context: context)
.copyWith(
fontWeight: FontWeight.w600,
color: Constant.textBlack),
),
),
),
SizedBox(
height: Constant.getActualYPhone(context: context, y: 4),
),
Padding(
padding: EdgeInsets.only(
top: Constant.getActualYPhone(context: context, y: 5),
left: Constant.getActualXPhone(context: context, x: 20),
right: Constant.getActualXPhone(context: context, x: 20),
),
child: TextField(
obscureText: obscureTextOldPassword.value,
controller: ctrlOldPassword,
decoration: InputDecoration(
suffixIcon: IconButton(
icon: Icon(
obscureTextOldPassword.value
? Icons.visibility
: Icons.visibility_off,
color: Constant.textGreyv2,
),
onPressed: () {
obscureTextOldPassword.value = !obscureTextOldPassword.value;
},
),
hintStyle:
Constant.body2_400(context: context).copyWith(
color: Constant.textGreyv2,
),
labelStyle:
Constant.body2_400(context: context).copyWith(
color: Constant.textGreyv2,
),
border: OutlineInputBorder(
borderSide: BorderSide(
color: Colors.orange,
width: 1,
),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Constant.textGreyv2,
width: 1,
),
),
labelText: "OldPassword",
hintText: 'Old Password',
),
),
),
SizedBox(
height: Constant.getActualYPhone(context: context, y: 40),
),
//New password
Padding(
padding: EdgeInsets.only(
// top: Constant.getActualYPhone(context: context, y: 63),
left: Constant.getActualXPhone(context: context, x: 20),
right: Constant.getActualXPhone(context: context, x: 20),
),
child: Align(
alignment: Alignment.centerLeft,
child: Text(
'New Password',
style: Constant.body1_400_dibulan(context: context)
.copyWith(
fontWeight: FontWeight.w600,
color: Constant.textBlack),
),
),
),
SizedBox(
height: Constant.getActualYPhone(context: context, y: 4),
),
Padding(
padding: EdgeInsets.only(
top: Constant.getActualYPhone(context: context, y: 5),
left: Constant.getActualXPhone(context: context, x: 20),
right: Constant.getActualXPhone(context: context, x: 20),
),
child: TextField(
obscureText: obscureTextNewPassword.value,
controller: ctrlNewPassword,
decoration: InputDecoration(
suffixIcon: IconButton(
icon: Icon(
obscureTextNewPassword.value
? Icons.visibility
: Icons.visibility_off,
color: Constant.textGreyv2,
),
onPressed: () {
obscureTextNewPassword.value = !obscureTextNewPassword.value;
},
),
hintStyle:
Constant.body2_400(context: context).copyWith(
color: Constant.textGreyv2,
),
labelStyle:
Constant.body2_400(context: context).copyWith(
color: Constant.textGreyv2,
),
border: OutlineInputBorder(
borderSide: BorderSide(
color: Colors.orange,
width: 1,
),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Constant.textGreyv2,
width: 1,
),
),
labelText: "New Password",
hintText: 'New Password',
),
),
),
SizedBox(
height: Constant.getActualYPhone(context: context, y: 40),
),
// Confirm password
Padding(
padding: EdgeInsets.only(
// top: Constant.getActualYPhone(context: context, y: 63),
left: Constant.getActualXPhone(context: context, x: 20),
right: Constant.getActualXPhone(context: context, x: 20),
),
child: Align(
alignment: Alignment.centerLeft,
child: Text(
'Confirm Password',
style: Constant.body1_400_dibulan(context: context)
.copyWith(
fontWeight: FontWeight.w600,
color: Constant.textBlack),
),
),
),
SizedBox(
height: Constant.getActualYPhone(context: context, y: 4),
),
Padding(
padding: EdgeInsets.only(
top: Constant.getActualYPhone(context: context, y: 5),
left: Constant.getActualXPhone(context: context, x: 20),
right: Constant.getActualXPhone(context: context, x: 20),
),
child: TextField(
obscureText: obscureTextConfirmPassword.value,
controller: ctrlConfirmPassword,
decoration: InputDecoration(
suffixIcon: IconButton(
icon: Icon(
obscureTextConfirmPassword.value
? Icons.visibility
: Icons.visibility_off,
color: Constant.textGreyv2,
),
onPressed: () {
obscureTextConfirmPassword.value = !obscureTextConfirmPassword.value;
},
),
hintStyle:
Constant.body2_400(context: context).copyWith(
color: Constant.textGreyv2,
),
labelStyle:
Constant.body2_400(context: context).copyWith(
color: Constant.textGreyv2,
),
border: OutlineInputBorder(
borderSide: BorderSide(
color: Colors.orange,
width: 1,
),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Constant.textGreyv2,
width: 1,
),
),
labelText: "Confirm Password",
hintText: 'Confirm Password',
),
),
),
SizedBox(
height: Constant.getActualYPhone(context: context, y: 50),
),
//Button Update Password
Padding(
padding: EdgeInsets.only(
left: Constant.getActualXPhone(context: context, x: 20),
right: Constant.getActualXPhone(context: context, x: 20),
),
child: SizedBox(
width: Constant.getActualXPhone(context: context, x: 390),
height: Constant.getActualYPhone(context: context, y: 50),
child: ElevatedButton(
onPressed: (loading.value == true)
? null
: () {
if (ctrlNewPassword.text !=
ctrlConfirmPassword.text) {
SanckbarWidget(
context,
'New Password dan dan confirm password tidak sama!',
snackbarType.warning);
return;
}
if (ctrlOldPassword.text ==
ctrlConfirmPassword.text) {
SanckbarWidget(
context,
'Password lama dan baru tidak boleh sama',
snackbarType.warning);
return;
}
if (ctrlOldPassword.text.isEmpty ||
ctrlNewPassword.text.isEmpty ||
ctrlConfirmPassword.text.isEmpty) {
isLoading.value = true;
SanckbarWidget(context, 'Inputan harus diisi',
snackbarType.warning);
} else {
ref
.read(ChangePasswordProvider.notifier)
.postChangePassword(
userTokenx: tokenx,
userToken: userToken,
userID: userID,
oldPassword: ctrlOldPassword.text,
newPassword: ctrlNewPassword.text,
confirmPassword:
ctrlConfirmPassword.text,
);
}
},
style: ButtonStyle(
backgroundColor: MaterialStateColor.resolveWith(
(st) => (loading.value == true)
? Constant.textGrey
: Constant.pcBtnBackgroundColor),
shape:
MaterialStateProperty.all<RoundedRectangleBorder>(
RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
side: BorderSide(
color: Constant.pcBtnBackgroundColor,
),
),
),
shadowColor:
MaterialStateProperty.all(Color(0xffff48423d)),
elevation: MaterialStateProperty.all(4.0),
),
child: Stack(
children: [
(isLoading.value)
? SizedBox(
width: Constant.getActualXPhone(
context: context, x: 24),
height: Constant.getActualYPhone(
context: context, y: 32),
child: Center(
child: CircularProgressIndicator(
color: Colors.white,
),
),
)
: Align(
alignment: Alignment.center,
child: Text(
'Submit',
style: Constant.titleH3_700(
context: context)
.copyWith(
color: Constant.textLoginColor),
),
),
],
),
),
),
),
],
),
),
),
),
),
);
}
}

View File

@@ -1,17 +1,56 @@
import 'dart:convert';
import 'package:app_petty_cash/screen/home/info_account_balance_provider.dart';
import 'package:app_petty_cash/screen/home/list_transaksi_home_provider.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:intl/intl.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../../app/constant.dart';
import '../../app/route.dart';
import '../../model/info_account_balance.dart';
import '../../model/list_transaksi_model.dart';
import '../../provider/current_info_account_balance_provider.dart';
import '../../provider/current_user_provider.dart';
import '../../widget/custom_drawer.dart';
import '../../widget/history_row_atas_widget.dart';
import '../../widget/sankbar_widget.dart';
class HomeScreen extends HookConsumerWidget {
const HomeScreen({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final M_CompanyID = useState<String>("0");
final searchTransaksiHomeLoading = useState<bool>(false);
final infoAccountBalanceHomeLoading = useState<bool>(false);
final listTransaksiHome = useState<List<ListTransaksiHomeModel>>(
List.empty(growable: true),
);
final M_CompanyName = useState("-");
// company name
Future<String> getCompanyName() async {
final shared = await SharedPreferences.getInstance();
String M_CompanyName = "-";
if (shared != null) {
final bearerString = shared.get(Constant.bearerName).toString();
final xmodel = jsonDecode(bearerString);
if (xmodel != null) {
M_CompanyName = xmodel["model"]["M_CompanyName"];
}
}
if (M_CompanyName == "0") {
// throw BaseRepositoryException(message: 'Invalid Company ID');
}
return M_CompanyName;
}
useEffect(() {
WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
final userID = ref.read(currentUserProvider)?.model.M_UserID ?? "0";
@@ -23,22 +62,456 @@ class HomeScreen extends HookConsumerWidget {
// Navigator.popAndPushNamed(context, loginRoute);
return;
}
M_CompanyName.value = await getCompanyName();
});
return () {};
}, []);
Future<String> getCompanyID() async {
final shared = await SharedPreferences.getInstance();
String M_CompanyID = "0";
if (shared != null) {
final bearerString = shared.get(Constant.bearerName).toString();
final xmodel = jsonDecode(bearerString);
if (xmodel != null) {
M_CompanyID = xmodel["model"]["M_CompanyID"];
}
}
if (M_CompanyID == "0") {
// throw BaseRepositoryException(message: 'Invalid Company ID');
}
return M_CompanyID;
}
useEffect(() {
WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
M_CompanyID.value = await getCompanyID();
// info account balance
ref
.read(infoAccountBalanceHomeProvider.notifier)
.infoAccountBalanceHome(M_CompanyID.value);
});
return () {};
}, []);
// read provider home
ref.listen(
listTransaksiHomeProvider,
(previous, next) {
if (next is ListTransaksiHomeStateLoading) {
searchTransaksiHomeLoading.value = true;
} else if (next is ListTransaksiHomeStateError) {
// print(next.message);
searchTransaksiHomeLoading.value = false;
SanckbarWidget(context, next.message, snackbarType.error);
} else if (next is ListTransaksiHomeStateDone) {
// print(jsonEncode(next.model));
// print(next.model.length);
listTransaksiHome.value = next.model;
searchTransaksiHomeLoading.value = false;
}
},
);
// read provider info account balance
ref.listen(
infoAccountBalanceHomeProvider,
(previous, next) {
if (next is InfoAccountBalanceHomeStateLoading) {
infoAccountBalanceHomeLoading.value = true;
} else if (next is InfoAccountBalanceHomeStateError) {
// print(next.message);
infoAccountBalanceHomeLoading.value = false;
SanckbarWidget(context, next.message, snackbarType.error);
} else if (next is InfoAccountBalanceHomeStateDone) {
// print(jsonEncode(next.model));
// print(next.model.length);
// infoAccountBalanceObject.value = next.model;
ref.read(currentInfoAccountBalanceProvider.notifier).state =
InfoAccountBalanceModel(
kredit: next.model.kredit,
debit: next.model.debit,
totalAll: next.model.totalAll,
);
infoAccountBalanceHomeLoading.value = false;
}
},
);
String formatDateString(String inputDate) {
try {
// Parsing tanggal dari string input
DateTime date =
DateFormat('dd-MM-yyyy HH:mm:ss', 'id').parse(inputDate);
// Format tanggal ke '30 Des 2023'
String formattedDate =
DateFormat('dd MMM yyyy HH:mm:ss', 'id').format(date);
return formattedDate;
} catch (e) {
// Tangkap kesalahan jika format tanggal tidak sesuai
print('Error parsing date: $e');
// return 'Format Tanggal Salah';
return inputDate;
}
}
return Padding(
padding: EdgeInsets.only(
top: Constant.getActualYPhone(context: context, y: 30),
),
child: Scaffold(
appBar: AppBar(
title: Text('Home Screen'),
title: Text(
// 'Home Screen',
M_CompanyName.value,
overflow: TextOverflow.ellipsis,
style: TextStyle(
color: Constant.textWhite,
),
),
backgroundColor: Constant.pcBtnBackgroundColor,
iconTheme: IconThemeData(
color: Constant.textWhite,
),
),
drawer: CustomDrawer(),
body: SafeArea(
child: Center(
child: Text('Home Screen Content'),
child: Padding(
padding: EdgeInsets.only(
top: Constant.getActualYPhone(context: context, y: 36),
left: Constant.getActualXPhone(context: context, x: 34),
right: Constant.getActualXPhone(context: context, x: 34),
),
child: RefreshIndicator(
onRefresh: () async {
ref
.read(infoAccountBalanceHomeProvider.notifier)
.infoAccountBalanceHome(M_CompanyID.value);
},
child: Container(
width: Constant.getActualXPhone(context: context, x: 390),
height: Constant.getActualYPhone(context: context, y: 844),
child: Column(
children: [
Container(
width: Constant.getActualXPhone(context: context, x: 332),
height:
Constant.getActualYPhone(context: context, y: 173),
decoration: BoxDecoration(
// color: Colors.black12,
borderRadius: BorderRadius.circular(20),
image: DecorationImage(
image: AssetImage('images/background_card.png'),
fit: BoxFit.cover,
),
boxShadow: [
BoxShadow(
color: Color.fromRGBO(145, 158, 171, 0.16),
offset: Offset(0, 24),
blurRadius: 48,
),
],
),
//Isi Card
child: (infoAccountBalanceHomeLoading.value)
? Center(
child: CircularProgressIndicator(),
)
: Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// Text1 & Text2
Padding(
padding: EdgeInsets.only(
top: Constant.getActualYPhone(
context: context, y: 17),
left: Constant.getActualXPhone(
context: context, x: 81),
right: Constant.getActualXPhone(
context: context, x: 81),
),
child: Column(
children: [
Text(
'Account Balance',
style: Constant.body1(
context: context)
.copyWith(
fontWeight: FontWeight.w400,
color: Constant.textWhite),
),
Text(
ref
.watch(
currentInfoAccountBalanceProvider)
.totalAll
.toString(),
style: Constant.H4_700V3(
context: context)
.copyWith(
color: Constant.textWhite,
),
),
],
),
),
SizedBox(
height: Constant.getActualYPhone(
context: context, y: 27),
),
//Debit dan Kredit
Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
// Spasi di sebelah kiri "Check In"
Expanded(
child: Padding(
padding: EdgeInsets.only(
left: Constant.getActualXPhone(
context: context, x: 20),
),
child: Column(
children: [
Text(
'Debit',
style: Constant.body1(
context: context)
.copyWith(
fontWeight:
FontWeight.w400,
color:
Constant.textWhite),
),
Text(
ref
.watch(
currentInfoAccountBalanceProvider)
.debit
.toString(),
style: Constant.H4_700V3(
context: context)
.copyWith(
color:
Constant.textWhite),
),
],
),
),
),
// SizedBox(
// width: Constant.getActualXPhone(
// context: context, x: 51),
// ),
Expanded(
child: Padding(
padding: EdgeInsets.only(
right: Constant.getActualXPhone(
context: context, x: 28),
),
child: Column(
children: [
Text(
'Credit',
style: Constant.body1(
context: context)
.copyWith(
fontWeight:
FontWeight.w400,
color:
Constant.textWhite),
),
Text(
ref
.watch(
currentInfoAccountBalanceProvider)
.kredit
.toString(),
style: Constant.H4_700V3(
context: context)
.copyWith(
color:
Constant.textWhite),
),
],
),
),
),
],
),
SizedBox(
height: Constant.getActualYPhone(
context: context, y: 17),
),
],
),
),
),
SizedBox(
height: Constant.getActualYPhone(context: context, y: 44),
),
Align(
alignment: Alignment.centerLeft,
child: Text(
'Transaksi Terkini',
style: Constant.titleH5_600(context: context)
.copyWith(color: Constant.textBlack),
),
),
SizedBox(
height: Constant.getActualYPhone(context: context, y: 24),
),
// list transaksi
(searchTransaksiHomeLoading.value)
? Center(
child: CircularProgressIndicator(),
)
: Container(
child: Expanded(
child: ListView.builder(
itemCount: listTransaksiHome.value.length,
itemBuilder: (context, idx) {
return Padding(
padding: EdgeInsets.only(
bottom: Constant.getActualYPhone(
context: context,
y: 10,
),
),
child: Card(
elevation: 2.0,
child: Padding(
padding: EdgeInsets.all(10),
child: Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// mainAxisAlignment: MainAxisAlignment.start,
children: [
// atas
HistoryRowAtasWidget(
icon_category_id:
listTransaksiHome
.value[idx].kategoriid
.toString(),
icon_category_name:
listTransaksiHome
.value[idx].kategoriname
.toString(),
amount: listTransaksiHome
.value[idx].amount
.toString(),
tglTransaksi: listTransaksiHome
.value[idx].tanggaltransaksi
.toString(),
tipe: listTransaksiHome
.value[idx].tipe
.toString(),
),
SizedBox(
height: Constant.getActualYPhone(
context: context, y: 10),
),
// catatan
Align(
alignment: Alignment.centerLeft,
child: Text(
listTransaksiHome
.value[idx].note
.toString(),
style: Constant.body1(
context: context)
.copyWith(
fontWeight: FontWeight.w600,
color: Constant.textBlack,
),
),
),
SizedBox(
height: Constant.getActualYPhone(
context: context, y: 10),
),
// Tanggal Created and by align right
Row(
mainAxisAlignment:
MainAxisAlignment.end,
children: [
Text(
'created : ' +
formatDateString(
listTransaksiHome
.value[idx]
.tanggalcreated
.toString(),
),
overflow:
TextOverflow.ellipsis,
style: Constant.body1(
context: context)
.copyWith(
fontWeight:
FontWeight.normal,
fontStyle: FontStyle.italic,
color: Constant.textBlack,
fontSize: 14,
),
),
Expanded(
child: Container(
child: Text(
' by : ' +
listTransaksiHome
.value[idx]
.usertransaksi
.toString(),
overflow:
TextOverflow.ellipsis,
style: Constant.body1(
context: context)
.copyWith(
fontWeight:
FontWeight.normal,
fontStyle:
FontStyle.italic,
color: Constant
.pcBtnBackgroundColor,
fontSize: 14,
),
),
),
),
],
),
],
),
),
),
);
},
),
),
),
],
),
),
),
),
),
),

View File

@@ -0,0 +1,75 @@
import 'package:app_petty_cash/provider/current_info_account_balance_provider.dart';
import 'package:app_petty_cash/repository/home_repository.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../model/info_account_balance.dart';
import '../../provider/dio_provider.dart';
import '../../repository/base_repository.dart';
import 'list_transaksi_home_provider.dart';
abstract class InfoAccountBalanceHomeState extends Equatable {
final DateTime date;
const InfoAccountBalanceHomeState(this.date);
@override
List<Object?> get props => [date];
}
class InfoAccountBalanceHomeStateInit extends InfoAccountBalanceHomeState {
InfoAccountBalanceHomeStateInit() : super(DateTime.now());
}
class InfoAccountBalanceHomeStateLoading extends InfoAccountBalanceHomeState {
InfoAccountBalanceHomeStateLoading() : super(DateTime.now());
}
class InfoAccountBalanceHomeStateError extends InfoAccountBalanceHomeState {
final String message;
InfoAccountBalanceHomeStateError({
required this.message,
}) : super(DateTime.now());
}
class InfoAccountBalanceHomeStateDone extends InfoAccountBalanceHomeState {
final InfoAccountBalanceModel model;
// final String resp;
InfoAccountBalanceHomeStateDone({
required this.model,
}) : super(DateTime.now());
}
//notifier
class InfoAccountBalanceHomeNotifier
extends StateNotifier<InfoAccountBalanceHomeState> {
final Ref ref;
InfoAccountBalanceHomeNotifier({
required this.ref,
}) : super(InfoAccountBalanceHomeStateInit());
void infoAccountBalanceHome(
String companyid,
) async {
try {
state = InfoAccountBalanceHomeStateLoading();
final dio = ref.read(dioProvider);
final resp = await HomeRepository(dio: dio).getInfoAccountBalance(
companyid,
);
state = InfoAccountBalanceHomeStateDone(model: resp);
// list transaksi home
ref.read(listTransaksiHomeProvider.notifier).listTransaksiHome(companyid);
} catch (e) {
if (e is BaseRepositoryException) {
state = InfoAccountBalanceHomeStateError(message: e.message.toString());
} else {
state = InfoAccountBalanceHomeStateError(message: e.toString());
}
}
}
}
//provider
final infoAccountBalanceHomeProvider = StateNotifierProvider<
InfoAccountBalanceHomeNotifier, InfoAccountBalanceHomeState>(
(ref) => InfoAccountBalanceHomeNotifier(ref: ref),
);

View File

@@ -0,0 +1,72 @@
import 'package:app_petty_cash/repository/home_repository.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../model/list_transaksi_model.dart';
import '../../repository/transaksi_repository.dart';
import '../../provider/dio_provider.dart';
import '../../repository/base_repository.dart';
abstract class ListTransaksiHomeState extends Equatable {
final DateTime date;
const ListTransaksiHomeState(this.date);
@override
List<Object?> get props => [date];
}
class ListTransaksiHomeStateInit extends ListTransaksiHomeState {
ListTransaksiHomeStateInit() : super(DateTime.now());
}
class ListTransaksiHomeStateLoading extends ListTransaksiHomeState {
ListTransaksiHomeStateLoading() : super(DateTime.now());
}
class ListTransaksiHomeStateError extends ListTransaksiHomeState {
final String message;
ListTransaksiHomeStateError({
required this.message,
}) : super(DateTime.now());
}
class ListTransaksiHomeStateDone extends ListTransaksiHomeState {
final List<ListTransaksiHomeModel> model;
// final String resp;
ListTransaksiHomeStateDone({
required this.model,
}) : super(DateTime.now());
}
//notifier
class ListTransaksiHomeNotifier extends StateNotifier<ListTransaksiHomeState> {
final Ref ref;
ListTransaksiHomeNotifier({
required this.ref,
}) : super(ListTransaksiHomeStateInit());
void listTransaksiHome(
String companyid,
) async {
try {
state = ListTransaksiHomeStateLoading();
final dio = ref.read(dioProvider);
final resp = await HomeRepository(dio: dio).getTransaksiListHome(
companyid,
);
state = ListTransaksiHomeStateDone(model: resp);
} catch (e) {
if (e is BaseRepositoryException) {
state = ListTransaksiHomeStateError(message: e.message.toString());
} else {
state = ListTransaksiHomeStateError(message: e.toString());
}
}
}
}
//provider
final listTransaksiHomeProvider =
StateNotifierProvider<ListTransaksiHomeNotifier, ListTransaksiHomeState>(
(ref) => ListTransaksiHomeNotifier(ref: ref),
);

View File

@@ -2,6 +2,7 @@
import 'dart:async';
import 'package:app_petty_cash/widget/sankbar_widget.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
@@ -71,10 +72,11 @@ class LoginFormScreen extends HookConsumerWidget {
isLoading.value = true;
} else if (next is LoginStateError) {
isLoading.value = false;
errorMessage.value = next.message;
Timer(const Duration(seconds: 3), () {
errorMessage.value = "";
});
// errorMessage.value = next.message;
// Timer(const Duration(seconds: 3), () {
// errorMessage.value = "";
// });
SanckbarWidget(context, next.message, snackbarType.warning);
} else if (next is LoginStateDone) {
isLoading.value = false;
isSuccess.value = true;
@@ -92,295 +94,297 @@ class LoginFormScreen extends HookConsumerWidget {
}
});
return Scaffold(
body: SafeArea(
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
Container(
width: Constant.getActualXPhone(context: context, x: 390),
height: Constant.getActualYPhone(context: context, y: 359),
// decoration: BoxDecoration(color: Colors.grey),
child: Image.asset(
"images/login_top_bg.png",
fit: BoxFit.fill,
// scale: 1,
),
final obscureText = useState<bool>(true);
return ListView(
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
Container(
width: Constant.getActualXPhone(context: context, x: 390),
height: Constant.getActualYPhone(context: context, y: 359),
// decoration: BoxDecoration(color: Colors.grey),
child: Image.asset(
"images/login_top_bg.png",
fit: BoxFit.fill,
// scale: 1,
),
Container(
width: Constant.getActualXPhone(context: context, x: 390),
height: Constant.getActualYPhone(context: context, y: 485),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
Padding(
padding: EdgeInsets.only(
top: Constant.getActualYPhone(context: context, y: 63),
left: Constant.getActualXPhone(context: context, x: 20),
right:
Constant.getActualXPhone(context: context, x: 113),
),
child: Text(
'Selamat Datang',
style: Constant.titleH1Login_SelamatDatang(
context: context)
.copyWith(
fontWeight: FontWeight.w600,
color: Constant.textBlack),
),
),
Container(
width: Constant.getActualXPhone(context: context, x: 390),
// height: Constant.getActualYPhone(context: context, y: 485),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
Padding(
padding: EdgeInsets.only(
top: Constant.getActualYPhone(context: context, y: 63),
left: Constant.getActualXPhone(context: context, x: 20),
right: Constant.getActualXPhone(context: context, x: 113),
),
Padding(
padding: EdgeInsets.only(
// top: Constant.getActualYPhone(context: context, y: 63),
left: Constant.getActualXPhone(context: context, x: 20),
right:
Constant.getActualXPhone(context: context, x: 48),
),
child: Text(
'Silahkan masuk untuk mengakses akun Anda',
style: Constant.body1(context: context).copyWith(
fontWeight: FontWeight.w600,
color: Constant.textGreyv2),
),
child: Text(
'Selamat Datang',
style:
Constant.titleH1Login_SelamatDatang(context: context)
.copyWith(
fontWeight: FontWeight.w600,
color: Constant.textBlack),
),
SizedBox(
height: Constant.getActualYPhone(context: context, y: 39),
),
Padding(
padding: EdgeInsets.only(
// top: Constant.getActualYPhone(context: context, y: 63),
left: Constant.getActualXPhone(context: context, x: 20),
right: Constant.getActualXPhone(context: context, x: 48),
),
child: Text(
'Silahkan masuk untuk mengakses akun Anda',
style: Constant.body1(context: context).copyWith(
fontWeight: FontWeight.w600,
color: Constant.textGreyv2),
),
),
SizedBox(
height: Constant.getActualYPhone(context: context, y: 39),
),
// inputan email
Padding(
padding: EdgeInsets.only(
// top: Constant.getActualYPhone(context: context, y: 63),
left: Constant.getActualXPhone(context: context, x: 20),
right:
Constant.getActualXPhone(context: context, x: 20),
),
child: Text(
'Email',
style: Constant.body1_400_dibulan(context: context)
.copyWith(
fontWeight: FontWeight.w600,
color: Constant.textBlack),
// inputan email
Padding(
padding: EdgeInsets.only(
// top: Constant.getActualYPhone(context: context, y: 63),
left: Constant.getActualXPhone(context: context, x: 20),
right: Constant.getActualXPhone(context: context, x: 20),
),
child: Text(
'Email',
style: Constant.body1_400_dibulan(context: context)
.copyWith(
fontWeight: FontWeight.w600,
color: Constant.textBlack),
),
),
SizedBox(
height: Constant.getActualYPhone(context: context, y: 4),
),
Padding(
padding: EdgeInsets.only(
top: Constant.getActualYPhone(context: context, y: 5),
left: Constant.getActualXPhone(context: context, x: 20),
right: Constant.getActualXPhone(context: context, x: 20),
),
child: TextField(
controller: ctrlEmail,
decoration: InputDecoration(
hintStyle:
Constant.body2_400(context: context).copyWith(
color: Constant.textGreyv2,
),
labelStyle:
Constant.body2_400(context: context).copyWith(
color: Constant.textGreyv2,
),
border: OutlineInputBorder(
borderSide: BorderSide(
color: Colors.orange,
width: 1,
),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Constant.textGreyv2,
width: 1,
),
),
labelText: "Email",
hintText: 'Email',
),
),
SizedBox(
height: Constant.getActualYPhone(context: context, y: 4),
),
SizedBox(
height: Constant.getActualYPhone(context: context, y: 21),
),
// password
Padding(
padding: EdgeInsets.only(
// top: Constant.getActualYPhone(context: context, y: 63),
left: Constant.getActualXPhone(context: context, x: 20),
right: Constant.getActualXPhone(context: context, x: 20),
),
Padding(
padding: EdgeInsets.only(
// top: Constant.getActualYPhone(context: context, y: 63),
left: Constant.getActualXPhone(context: context, x: 20),
right:
Constant.getActualXPhone(context: context, x: 20),
),
child: TextField(
controller: ctrlEmail,
decoration: InputDecoration(
hintStyle:
Constant.body2_400(context: context).copyWith(
child: Text(
'Password',
style: Constant.body1_400_dibulan(context: context)
.copyWith(
fontWeight: FontWeight.w600,
color: Constant.textBlack),
),
),
SizedBox(
height: Constant.getActualYPhone(context: context, y: 4),
),
Padding(
padding: EdgeInsets.only(
top: Constant.getActualYPhone(context: context, y: 5),
left: Constant.getActualXPhone(context: context, x: 20),
right: Constant.getActualXPhone(context: context, x: 20),
),
child: TextField(
obscureText: obscureText.value,
controller: ctrlPassword,
decoration: InputDecoration(
suffixIcon: IconButton(
icon: Icon(
obscureText.value
? Icons.visibility
: Icons.visibility_off,
color: Constant.textGreyv2,
),
labelStyle:
Constant.body2_400(context: context).copyWith(
onPressed: () {
obscureText.value = !obscureText.value;
},
),
hintStyle:
Constant.body2_400(context: context).copyWith(
color: Constant.textGreyv2,
),
labelStyle:
Constant.body2_400(context: context).copyWith(
color: Constant.textGreyv2,
),
border: OutlineInputBorder(
borderSide: BorderSide(
color: Colors.orange,
width: 1,
),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Constant.textGreyv2,
width: 1,
),
border: OutlineInputBorder(
borderSide: BorderSide(
color: Colors.orange,
width: 1,
),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Constant.textGreyv2,
width: 1,
),
),
// labelText: "Masukkan Email",
// hintText: 'Nama Pengirim',
),
labelText: "Password",
hintText: 'Password',
),
),
),
SizedBox(
height: Constant.getActualYPhone(context: context, y: 21),
),
// if (errorMessage.value != "") ...[
// SizedBox(
// height: Constant.getActualYPhone(context: context, y: 10),
// ),
// Center(
// child: Text(
// "Peringatan : ${errorMessage.value}",
// style: Constant.titleH7_700(context: context)
// .copyWith(color: Constant.textRedProblemMaketing),
// ),
// ),
// ],
// password
Padding(
padding: EdgeInsets.only(
// top: Constant.getActualYPhone(context: context, y: 63),
left: Constant.getActualXPhone(context: context, x: 20),
right:
Constant.getActualXPhone(context: context, x: 20),
),
child: Text(
'Password',
style: Constant.body1_400_dibulan(context: context)
.copyWith(
fontWeight: FontWeight.w600,
color: Constant.textBlack),
),
SizedBox(
height: Constant.getActualYPhone(context: context, y: 40),
),
Padding(
padding: EdgeInsets.only(
left: Constant.getActualXPhone(context: context, x: 20),
right: Constant.getActualXPhone(context: context, x: 20),
),
SizedBox(
height: Constant.getActualYPhone(context: context, y: 4),
),
Padding(
padding: EdgeInsets.only(
// top: Constant.getActualYPhone(context: context, y: 63),
left: Constant.getActualXPhone(context: context, x: 20),
right:
Constant.getActualXPhone(context: context, x: 20),
),
child: TextField(
controller: ctrlPassword,
decoration: InputDecoration(
hintStyle:
Constant.body2_400(context: context).copyWith(
color: Colors.orange,
),
labelStyle:
Constant.body2_400(context: context).copyWith(
color: Colors.orange,
),
border: OutlineInputBorder(
borderSide: BorderSide(
color: Colors.orange,
width: 1,
),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Colors.orange,
width: 1,
),
),
// labelText: "Nama Pengirim",
// hintText: 'Nama Pengirim',
),
),
),
if (errorMessage.value != "") ...[
SizedBox(
height:
Constant.getActualYPhone(context: context, y: 10),
),
Center(
child: Text(
"Peringatan : ${errorMessage.value}",
style: Constant.titleH7_700(context: context)
.copyWith(color: Constant.textRedProblemMaketing),
),
),
],
SizedBox(
height: Constant.getActualYPhone(context: context, y: 40),
),
Padding(
padding: EdgeInsets.only(
left: Constant.getActualXPhone(context: context, x: 20),
right:
Constant.getActualXPhone(context: context, x: 20),
),
child: SizedBox(
width:
Constant.getActualXPhone(context: context, x: 390),
height:
Constant.getActualYPhone(context: context, y: 50),
child: ElevatedButton(
// onPressed: () {
// Navigator.of(context).pushNamed(homeRoute);
// },
onPressed: (isSuccess.value == true)
? () {}
: () {
if (ctrlEmail.text.isEmpty ||
ctrlPassword.text.isEmpty) {
isLoading.value = true;
errorMessage.value = 'Inputan harus diisi';
Timer(const Duration(seconds: 3), () {
isLoading.value = false;
errorMessage.value = "";
});
} else {
ref.read(loginProvider.notifier).login(
email: ctrlEmail.text,
password: ctrlPassword.text,
);
}
},
style: ButtonStyle(
backgroundColor: MaterialStateColor.resolveWith(
(st) => (isSuccess.value == true)
? Constant.textGrey
: Constant.pcBtnBackgroundColor),
shape: MaterialStateProperty.all<
RoundedRectangleBorder>(
RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
side: BorderSide(
color: Constant.pcBtnBackgroundColor,
),
child: SizedBox(
width: Constant.getActualXPhone(context: context, x: 390),
height: Constant.getActualYPhone(context: context, y: 50),
child: ElevatedButton(
// onPressed: () {
// Navigator.of(context).pushNamed(homeRoute);
// },
onPressed: (isSuccess.value == true)
? () {}
: () {
if (ctrlEmail.text.isEmpty ||
ctrlPassword.text.isEmpty) {
isLoading.value = true;
errorMessage.value = 'Inputan harus diisi';
Timer(const Duration(seconds: 3), () {
isLoading.value = false;
errorMessage.value = "";
});
} else {
ref.read(loginProvider.notifier).login(
email: ctrlEmail.text,
password: ctrlPassword.text,
);
}
},
style: ButtonStyle(
backgroundColor: MaterialStateColor.resolveWith(
(st) => (isSuccess.value == true)
? Constant.textGrey
: Constant.pcBtnBackgroundColor),
shape:
MaterialStateProperty.all<RoundedRectangleBorder>(
RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
side: BorderSide(
color: Constant.pcBtnBackgroundColor,
),
),
shadowColor:
MaterialStateProperty.all(Color(0xffff48423d)),
elevation: MaterialStateProperty.all(4.0),
),
child: Stack(
children: [
(isLoading.value)
? SizedBox(
width: Constant.getActualXPhone(
context: context, x: 24),
height: Constant.getActualYPhone(
context: context, y: 32),
child: Center(
child: CircularProgressIndicator(
color: Colors.white,
),
),
)
: Align(
alignment: Alignment.center,
child: Text(
'Login',
style: Constant.titleH3_700(
context: context)
.copyWith(
color: Constant.textLoginColor),
shadowColor:
MaterialStateProperty.all(Color(0xffff48423d)),
elevation: MaterialStateProperty.all(4.0),
),
child: Stack(
children: [
(isLoading.value)
? SizedBox(
width: Constant.getActualXPhone(
context: context, x: 24),
height: Constant.getActualYPhone(
context: context, y: 32),
child: Center(
child: CircularProgressIndicator(
color: Colors.white,
),
),
],
),
)
: Align(
alignment: Alignment.center,
child: Text(
'Login',
style: Constant.titleH3_700(
context: context)
.copyWith(
color: Constant.textLoginColor),
),
),
],
),
),
),
),
SizedBox(
height: Constant.getActualYPhone(context: context, y: 10),
),
SizedBox(
height: Constant.getActualYPhone(context: context, y: 10),
),
Align(
alignment: Alignment.center,
child: Text(
'Versi ${Constant.version}',
style: Constant.titleH4Login(context: context)
.copyWith(color: Constant.textGrey),
),
Align(
alignment: Alignment.center,
child: Text(
'Versi ${Constant.version}',
style: Constant.titleH4Login(context: context)
.copyWith(color: Constant.textGrey),
),
],
),
),
],
),
],
),
),
],
),
),
],
);
}
}

View File

@@ -29,7 +29,8 @@ class LoginScreen extends HookConsumerWidget {
final authModel = AuthModel(
token: xmodel["token"],
model: AuthDoctorModel(
// M_UserM_DoctorID: xmodel["model"]["M_UserM_DoctorID"],
M_CompanyID: xmodel["model"]["M_CompanyID"],
M_CompanyName: xmodel["model"]["M_CompanyName"],
M_UserEmail: xmodel["model"]["M_UserEmail"],
M_UserUsername: xmodel["model"]["M_UserUsername"],
// M_UserM_DoctorCode: xmodel["model"]["M_UserM_DoctorCode"],
@@ -39,7 +40,7 @@ class LoginScreen extends HookConsumerWidget {
);
ref.read(currentUserProvider.notifier).state = authModel;
// ref.read(currentPageProvider.state).update((state) => 0);
ref.read(currentPageProvider.state).update((state) => 0);
Navigator.of(context).pushNamedAndRemoveUntil(
homeRoute,
@@ -54,22 +55,22 @@ class LoginScreen extends HookConsumerWidget {
onTap: () => FocusManager.instance.primaryFocus?.unfocus(),
child: Scaffold(
resizeToAvoidBottomInset: true,
backgroundColor: Constant.backgroundColor,
// backgroundColor: Colors.transparent,
body: SafeArea(
child: Center(
child: Container(
width: Constant.getActualXPhone(context: context, x: 390),
height: Constant.getActualYPhone(context: context, y: 844),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
Color(0xffdadae6),
Color(0xfff6f7fb),
],
begin: Alignment.centerLeft,
end: Alignment.centerRight,
),
),
// decoration: BoxDecoration(
// gradient: LinearGradient(
// colors: [
// Color(0xffdadae6),
// Color(0xfff6f7fb),
// ],
// begin: Alignment.centerLeft,
// end: Alignment.centerRight,
// ),
// ),
// child: BlockBodyV2(),
child: LoginFormScreen(),
),

View File

@@ -1,3 +1,7 @@
import 'dart:convert';
import 'package:shared_preferences/shared_preferences.dart';
import '../../widget/sankbar_widget.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
@@ -25,6 +29,30 @@ class ReportScreen extends HookConsumerWidget {
final tglAkhir = useState<DateTime>(DateTime.now());
final tglAkhirTmp = useState<String>("");
String M_CompanyID = "0";
String timeStamp = DateFormat('dd-MM-yyyy').format(
DateTime.now(),
);
Future<String> getCompanyID() async {
final shared = await SharedPreferences.getInstance();
String M_CompanyID = "0";
if (shared != null) {
final bearerString = shared.get(Constant.bearerName).toString();
final xmodel = jsonDecode(bearerString);
if (xmodel != null) {
M_CompanyID = xmodel["model"]["M_CompanyID"];
}
}
if (M_CompanyID == "0") {
// throw BaseRepositoryException(message: 'Invalid Company ID');
}
return M_CompanyID;
}
useEffect(() {
WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
@@ -47,350 +75,377 @@ class ReportScreen extends HookConsumerWidget {
),
child: Scaffold(
appBar: AppBar(
title: Text('Report'),
title: Text(
'Report',
style: TextStyle(color: Constant.textWhite),
),
backgroundColor: Constant.pcBtnBackgroundColor,
iconTheme: IconThemeData(
color: Constant.textWhite,
),
),
drawer: CustomDrawer(),
body: SafeArea(
child: SingleChildScrollView(
child: Padding(
padding: EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Tanggal Awal',
style: Constant.body1(context: context).copyWith(
fontWeight: FontWeight.w600, color: Constant.textBlack),
),
SizedBox(
height: Constant.getActualYPhone(context: context, y: 10),
),
// Tanggal Awal
Row(
children: [
Expanded(
child: TextField(
controller: ctrlTglAwal,
decoration: InputDecoration(
hintStyle:
Constant.body2_400(context: context).copyWith(
color: Colors.orange,
),
labelStyle:
Constant.body2_400(context: context).copyWith(
color: Colors.orange,
),
border: OutlineInputBorder(
borderSide: BorderSide(
color: Colors.orange,
width: 1,
),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Colors.orange,
width: 1,
),
),
// labelText: "Tanggal Awal",
hintText: 'Tanggal Awal',
// suffixIcon: isLoadingFilterScope.value
// ? SizedBox(
// width: Constant.getActualXPhone(
// context: context,
// x: 4,
// ),
// height: Constant.getActualYPhone(
// context: context,
// y: 4,
// ),
// child: CircularProgressIndicator(
// color: Constant.textRed,
// ),
// )
// : Icon(
// Icons.calendar_month_sharp,
// color: Constant.colorIconDate,
// ),
),
onTap: () async {
final selectedDateAwal = await showDatePicker(
// locale: const Locale("en-CA"),
// locale: ,
context: context,
initialEntryMode:
DatePickerEntryMode.calendarOnly,
firstDate: DateTime(2000),
lastDate: DateTime(2100),
initialDate: (ctrlTglAwal.text.isEmpty)
? DateTime.now()
: tglAwal.value,
);
if (selectedDateAwal != null) {
String formattedDate = DateFormat('dd-MM-yyyy')
.format(selectedDateAwal);
// ctrlTglAwal.text =
// selectedDateAwal.toString().split(' ')[0];
ctrlTglAwal.text = formattedDate;
tglAwal.value = selectedDateAwal;
tglAwalTmp.value = selectedDateAwal.toString();
}
if (selectedDateAwal == null) {
print('cancel button');
return;
}
},
),
),
],
),
SizedBox(
height: Constant.getActualYPhone(context: context, y: 20),
),
Text(
'Tanggal Akhir',
style: Constant.body1(context: context).copyWith(
fontWeight: FontWeight.w600, color: Constant.textBlack),
),
SizedBox(
height: Constant.getActualYPhone(context: context, y: 10),
),
// Tanggal Akhir
Row(
children: [
Expanded(
child: TextField(
controller: ctrlTglAkhir,
decoration: InputDecoration(
hintStyle:
Constant.body2_400(context: context).copyWith(
color: Colors.orange,
),
labelStyle:
Constant.body2_400(context: context).copyWith(
color: Colors.orange,
),
border: OutlineInputBorder(
borderSide: BorderSide(
color: Colors.orange,
width: 1,
),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Colors.orange,
width: 1,
),
),
// labelText: "Tanggal Awal",
hintText: 'Tanggal Akhir',
// suffixIcon: isLoadingFilterScope.value
// ? SizedBox(
// width: Constant.getActualXPhone(
// context: context,
// x: 4,
// ),
// height: Constant.getActualYPhone(
// context: context,
// y: 4,
// ),
// child: CircularProgressIndicator(
// color: Constant.textRed,
// ),
// )
// : Icon(
// Icons.calendar_month_sharp,
// color: Constant.colorIconDate,
// ),
),
onTap: () async {
final selectedDateAkhir = await showDatePicker(
// locale: const Locale("en-CA"),
// locale: ,
context: context,
initialEntryMode:
DatePickerEntryMode.calendarOnly,
firstDate: DateTime(2000),
lastDate: DateTime(2100),
initialDate: (ctrlTglAkhir.text.isEmpty)
? DateTime.now()
: tglAkhir.value,
);
if (selectedDateAkhir != null) {
String formattedDate = DateFormat('dd-MM-yyyy')
.format(selectedDateAkhir);
// ctrlTglAkhir.text =
// selectedDateAkhir.toString().split(' ')[0];
ctrlTglAkhir.text = formattedDate;
tglAkhir.value = selectedDateAkhir;
tglAkhirTmp.value = selectedDateAkhir.toString();
}
if (selectedDateAkhir == null) {
print('cancel button');
return;
}
},
),
),
],
),
],
),
),
),
),
bottomNavigationBar: Padding(
padding: EdgeInsets.only(
// right: Constant.getActualXPhone(context: context, x: 27),
// left: Constant.getActualXPhone(context: context, x: 27),
// bottom: Constant.getActualYPhone(context: context, y: 32),
top: Constant.getActualYPhone(context: context, y: 10),
),
child: BottomAppBar(
child: Container(
height: 150,
child: Padding(
padding: EdgeInsets.only(
// right: Constant.getActualXPhone(context: context, x: 27),
// left: Constant.getActualXPhone(context: context, x: 27),
// bottom: Constant.getActualYPhone(context: context, y: 32),
top: Constant.getActualYPhone(context: context, y: 24),
child: Padding(
padding: EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Tanggal Awal',
style: Constant.body1(context: context).copyWith(
fontWeight: FontWeight.w600, color: Constant.textBlack),
),
child: Column(
SizedBox(
height: Constant.getActualYPhone(context: context, y: 10),
),
// Tanggal Awal
Row(
children: [
// Excel
Container(
width: Constant.getActualXPhone(context: context, x: 336),
height: Constant.getActualYPhone(context: context, y: 42),
child: ElevatedButton(
style: ButtonStyle(
backgroundColor: MaterialStateColor.resolveWith(
(states) => Colors.white),
// side: MaterialStateBorderSide.resolveWith(
// (states) => BorderSide(color: Colors.green),
// ),
// backgroundColor: MaterialStateColor.resolveWith(
// (st) => Constant.pcBtnBackgroundColor),
shape:
MaterialStateProperty.all<RoundedRectangleBorder>(
RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
side: BorderSide(
color: Colors.green,
),
Expanded(
child: TextField(
controller: ctrlTglAwal,
decoration: InputDecoration(
hintStyle:
Constant.body2_400(context: context).copyWith(
color: Colors.orange,
),
labelStyle:
Constant.body2_400(context: context).copyWith(
color: Colors.orange,
),
border: OutlineInputBorder(
borderSide: BorderSide(
color: Colors.orange,
width: 1,
),
),
shadowColor:
MaterialStateProperty.all(Color(0xffff48423d)),
elevation: MaterialStateProperty.all(4.0),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
width: Constant.getActualXPhone(
context: context, x: 16),
height: Constant.getActualYPhone(
context: context, y: 16),
// decoration: BoxDecoration(color: Colors.grey),
child: Image.asset(
"images/logo_excel.png",
fit: BoxFit.fill,
// scale: 1,
),
),
SizedBox(
height: Constant.getActualXPhone(
context: context, x: 8),
),
Text(
'Download Report (xls) ',
style: Constant.body1(context: context).copyWith(
fontWeight: FontWeight.w600,
color: Colors.green,
),
),
],
),
onPressed: () {},
),
),
SizedBox(
height: Constant.getActualYPhone(context: context, y: 20),
),
// PDF
Container(
width: Constant.getActualXPhone(context: context, x: 336),
height: Constant.getActualYPhone(context: context, y: 42),
child: ElevatedButton(
style: ButtonStyle(
backgroundColor: MaterialStateColor.resolveWith(
(st) => Constant.pcBtnBackgroundColor),
shape:
MaterialStateProperty.all<RoundedRectangleBorder>(
RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
side: BorderSide(
color: Constant.pcBtnBackgroundColor,
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Colors.orange,
width: 1,
),
),
shadowColor:
MaterialStateProperty.all(Color(0xffff48423d)),
elevation: MaterialStateProperty.all(4.0),
// labelText: "Tanggal Awal",
hintText: 'Tanggal Awal',
// suffixIcon: isLoadingFilterScope.value
// ? SizedBox(
// width: Constant.getActualXPhone(
// context: context,
// x: 4,
// ),
// height: Constant.getActualYPhone(
// context: context,
// y: 4,
// ),
// child: CircularProgressIndicator(
// color: Constant.textRed,
// ),
// )
// : Icon(
// Icons.calendar_month_sharp,
// color: Constant.colorIconDate,
// ),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
width: Constant.getActualXPhone(
context: context, x: 16),
height: Constant.getActualYPhone(
context: context, y: 16),
// decoration: BoxDecoration(color: Colors.grey),
child: Image.asset(
"images/logo_pdf.png",
fit: BoxFit.fill,
// scale: 1,
),
),
SizedBox(
height: Constant.getActualXPhone(
context: context, x: 8),
),
Text(
'Download Report (PDF)',
style: Constant.body1(context: context).copyWith(
fontWeight: FontWeight.w600,
color: Constant.white),
),
],
),
onPressed: () async {
String url =
"https://pub.dev/packages?q=url+launcher";
if (!await launchUrl(Uri.parse(url))) {
// throw Exception('Could not launch $url');
SanckbarWidget(context, 'Could not launch $url',
snackbarType.error);
onTap: () async {
final selectedDateAwal = await showDatePicker(
// locale: const Locale("en-CA"),
// locale: ,
context: context,
initialEntryMode: DatePickerEntryMode.calendarOnly,
firstDate: DateTime(2000),
lastDate: DateTime(2100),
initialDate: (ctrlTglAwal.text.isEmpty)
? DateTime.now()
: tglAwal.value,
);
if (selectedDateAwal != null) {
String formattedDate = DateFormat('dd-MM-yyyy')
.format(selectedDateAwal);
// ctrlTglAwal.text =
// selectedDateAwal.toString().split(' ')[0];
ctrlTglAwal.text = formattedDate;
tglAwal.value = selectedDateAwal;
tglAwalTmp.value = selectedDateAwal.toString();
}
if (selectedDateAwal == null) {
print('cancel button');
return;
}
},
),
),
],
),
),
SizedBox(
height: Constant.getActualYPhone(context: context, y: 20),
),
Text(
'Tanggal Akhir',
style: Constant.body1(context: context).copyWith(
fontWeight: FontWeight.w600, color: Constant.textBlack),
),
SizedBox(
height: Constant.getActualYPhone(context: context, y: 10),
),
// Tanggal Akhir
Row(
children: [
Expanded(
child: TextField(
controller: ctrlTglAkhir,
decoration: InputDecoration(
hintStyle:
Constant.body2_400(context: context).copyWith(
color: Colors.orange,
),
labelStyle:
Constant.body2_400(context: context).copyWith(
color: Colors.orange,
),
border: OutlineInputBorder(
borderSide: BorderSide(
color: Colors.orange,
width: 1,
),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Colors.orange,
width: 1,
),
),
// labelText: "Tanggal Awal",
hintText: 'Tanggal Akhir',
// suffixIcon: isLoadingFilterScope.value
// ? SizedBox(
// width: Constant.getActualXPhone(
// context: context,
// x: 4,
// ),
// height: Constant.getActualYPhone(
// context: context,
// y: 4,
// ),
// child: CircularProgressIndicator(
// color: Constant.textRed,
// ),
// )
// : Icon(
// Icons.calendar_month_sharp,
// color: Constant.colorIconDate,
// ),
),
onTap: () async {
final selectedDateAkhir = await showDatePicker(
// locale: const Locale("en-CA"),
// locale: ,
context: context,
initialEntryMode: DatePickerEntryMode.calendarOnly,
firstDate: DateTime(2000),
lastDate: DateTime(2100),
initialDate: (ctrlTglAkhir.text.isEmpty)
? DateTime.now()
: tglAkhir.value,
);
if (selectedDateAkhir != null) {
String formattedDate = DateFormat('dd-MM-yyyy')
.format(selectedDateAkhir);
// ctrlTglAkhir.text =
// selectedDateAkhir.toString().split(' ')[0];
ctrlTglAkhir.text = formattedDate;
tglAkhir.value = selectedDateAkhir;
tglAkhirTmp.value = selectedDateAkhir.toString();
}
if (selectedDateAkhir == null) {
print('cancel button');
return;
}
},
),
),
],
),
Spacer(),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
ElevatedButton(
style: ButtonStyle(
backgroundColor: MaterialStateColor.resolveWith(
(states) => Colors.white),
// side: MaterialStateBorderSide.resolveWith(
// (states) => BorderSide(color: Colors.green),
// ),
// backgroundColor: MaterialStateColor.resolveWith(
// (st) => Constant.pcBtnBackgroundColor),
shape:
MaterialStateProperty.all<RoundedRectangleBorder>(
RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
side: BorderSide(
color: Colors.green,
),
),
),
shadowColor:
MaterialStateProperty.all(Color(0xffff48423d)),
elevation: MaterialStateProperty.all(4.0),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
width: Constant.getActualXPhone(
context: context, x: 16),
height: Constant.getActualYPhone(
context: context, y: 16),
// decoration: BoxDecoration(color: Colors.grey),
child: Image.asset(
"images/logo_excel.png",
fit: BoxFit.fill,
// scale: 1,
),
),
SizedBox(
width: Constant.getActualXPhone(
context: context, x: 10),
),
Text(
'Excel',
style: Constant.body1(context: context).copyWith(
fontWeight: FontWeight.w600,
color: Colors.green,
),
),
],
),
onPressed: () async {
M_CompanyID = await getCompanyID();
if (M_CompanyID == "0") {
SanckbarWidget(
context, 'Invalid Company', snackbarType.error);
return;
}
// Awal
DateTime parsedDateAwal =
DateFormat('dd-MM-yyyy').parse(
ctrlTglAwal.value.text.toString(),
);
String formattedDateAwal =
DateFormat('yyyy-MM-dd').format(parsedDateAwal);
// Akhir
DateTime parsedDateAkhir =
DateFormat('dd-MM-yyyy').parse(
ctrlTglAkhir.value.text.toString(),
);
String formattedDateAkhir =
DateFormat('yyyy-MM-dd').format(parsedDateAkhir);
String url =
"https://${Constant.baseUrlDevoneReport}/birt/run?__report=report/one/pettycash/rpt_r_pt_001.rptdesign&__format=xls&PStartDate=$formattedDateAwal&PEndDate=$formattedDateAkhir&PCompanyID=$M_CompanyID&username=adminsas%20&tm=$timeStamp";
if (!await launchUrl(Uri.parse(url))) {
// throw Exception('Could not launch $url');
SanckbarWidget(context, 'Could not launch $url',
snackbarType.error);
}
// https://devone.aplikasi.web.id/birt/run?__report=report/one/pettycash/rpt_r_pt_001.rptdesign&__format=pdf&PStartDate=2023-11-01&PEndDate=2023-12-30&PCompanyID=0&username=adminsas%20&tm=1701327096267
},
),
// PDF
ElevatedButton(
style: ButtonStyle(
backgroundColor: MaterialStateColor.resolveWith(
(st) => Constant.pcBtnBackgroundColor),
shape:
MaterialStateProperty.all<RoundedRectangleBorder>(
RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
side: BorderSide(
color: Constant.pcBtnBackgroundColor,
),
),
),
shadowColor:
MaterialStateProperty.all(Color(0xffff48423d)),
elevation: MaterialStateProperty.all(4.0),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
width: Constant.getActualXPhone(
context: context, x: 16),
height: Constant.getActualYPhone(
context: context, y: 16),
// decoration: BoxDecoration(color: Colors.grey),
child: Image.asset(
"images/logo_pdf.png",
fit: BoxFit.fill,
// scale: 1,
),
),
SizedBox(
width: Constant.getActualXPhone(
context: context, x: 8),
),
Text(
'PDF',
style: Constant.body1(context: context).copyWith(
fontWeight: FontWeight.w600,
color: Constant.white),
),
],
),
onPressed: () async {
M_CompanyID = await getCompanyID();
if (M_CompanyID == "0") {
SanckbarWidget(
context, 'Invalid Company', snackbarType.error);
return;
}
// Awal
DateTime parsedDateAwal =
DateFormat('dd-MM-yyyy').parse(
ctrlTglAwal.value.text.toString(),
);
String formattedDateAwal =
DateFormat('yyyy-MM-dd').format(parsedDateAwal);
// Akhir
DateTime parsedDateAkhir =
DateFormat('dd-MM-yyyy').parse(
ctrlTglAkhir.value.text.toString(),
);
String formattedDateAkhir =
DateFormat('yyyy-MM-dd').format(parsedDateAkhir);
String url =
"https://${Constant.baseUrlDevoneReport}/birt/run?__report=report/one/pettycash/rpt_r_pt_001.rptdesign&__format=pdf&PStartDate=$formattedDateAwal&PEndDate=$formattedDateAkhir&PCompanyID=$M_CompanyID&username=adminsas%20&tm=$timeStamp";
print(url);
if (!await launchUrl(Uri.parse(url))) {
// throw Exception('Could not launch $url');
SanckbarWidget(context, 'Could not launch $url',
snackbarType.error);
}
},
),
],
),
],
),
),
),

View File

@@ -0,0 +1,84 @@
import 'package:equatable/equatable.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../repository/transaksi_repository.dart';
import '../../provider/dio_provider.dart';
import '../../repository/base_repository.dart';
import 'search_history_transaksi_provider.dart';
abstract class ConfirmTransaksiState extends Equatable {
final DateTime date;
const ConfirmTransaksiState(this.date);
@override
List<Object?> get props => [date];
}
class ConfirmTransaksiStateInit extends ConfirmTransaksiState {
ConfirmTransaksiStateInit() : super(DateTime.now());
}
class ConfirmTransaksiStateLoading extends ConfirmTransaksiState {
ConfirmTransaksiStateLoading() : super(DateTime.now());
}
class ConfirmTransaksiStateError extends ConfirmTransaksiState {
final String message;
ConfirmTransaksiStateError({
required this.message,
}) : super(DateTime.now());
}
class ConfirmTransaksiStateDone extends ConfirmTransaksiState {
// final List<ListType> model;
final String resp;
ConfirmTransaksiStateDone({
required this.resp,
}) : super(DateTime.now());
}
//notifier
class ConfirmTransaksiNotifier extends StateNotifier<ConfirmTransaksiState> {
final Ref ref;
ConfirmTransaksiNotifier({
required this.ref,
}) : super(ConfirmTransaksiStateInit());
void confirmTransaksi(
String id,
String userid,
String companyid,
String tglAwal,
String tglAkhir,
String categoryid,
) async {
try {
state = ConfirmTransaksiStateLoading();
final dio = ref.read(dioProvider);
final resp = await TransaksiRepository(dio: dio).confirmTransaksi(
id,
userid,
);
state = ConfirmTransaksiStateDone(resp: resp);
// search lagi buat get data
ref.read(searchHistoryTransaksiProvider.notifier).searchHistoryTransaksi(
companyid,
tglAwal,
tglAkhir,
categoryid,
);
} catch (e) {
if (e is BaseRepositoryException) {
state = ConfirmTransaksiStateError(message: e.message.toString());
} else {
state = ConfirmTransaksiStateError(message: e.toString());
}
}
}
}
//provider
final confirmTransaksiProvider =
StateNotifierProvider<ConfirmTransaksiNotifier, ConfirmTransaksiState>(
(ref) => ConfirmTransaksiNotifier(ref: ref),
);

View File

@@ -0,0 +1,84 @@
import 'package:equatable/equatable.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../repository/transaksi_repository.dart';
import '../../provider/dio_provider.dart';
import '../../repository/base_repository.dart';
import 'search_history_transaksi_provider.dart';
abstract class DeleteTransaksiState extends Equatable {
final DateTime date;
const DeleteTransaksiState(this.date);
@override
List<Object?> get props => [date];
}
class DeleteTransaksiStateInit extends DeleteTransaksiState {
DeleteTransaksiStateInit() : super(DateTime.now());
}
class DeleteTransaksiStateLoading extends DeleteTransaksiState {
DeleteTransaksiStateLoading() : super(DateTime.now());
}
class DeleteTransaksiStateError extends DeleteTransaksiState {
final String message;
DeleteTransaksiStateError({
required this.message,
}) : super(DateTime.now());
}
class DeleteTransaksiStateDone extends DeleteTransaksiState {
// final List<ListType> model;
final String resp;
DeleteTransaksiStateDone({
required this.resp,
}) : super(DateTime.now());
}
//notifier
class DeleteTransaksiNotifier extends StateNotifier<DeleteTransaksiState> {
final Ref ref;
DeleteTransaksiNotifier({
required this.ref,
}) : super(DeleteTransaksiStateInit());
void deleteTransaksi(
String id,
String userid,
String companyid,
String tglAwal,
String tglAkhir,
String categoryid,
) async {
try {
state = DeleteTransaksiStateLoading();
final dio = ref.read(dioProvider);
final resp = await TransaksiRepository(dio: dio).deleteTransaksi(
id,
userid,
);
state = DeleteTransaksiStateDone(resp: resp);
// search lagi buat get data
ref.read(searchHistoryTransaksiProvider.notifier).searchHistoryTransaksi(
companyid,
tglAwal,
tglAkhir,
categoryid,
);
} catch (e) {
if (e is BaseRepositoryException) {
state = DeleteTransaksiStateError(message: e.message.toString());
} else {
state = DeleteTransaksiStateError(message: e.toString());
}
}
}
}
//provider
final deleteTransaksiProvider =
StateNotifierProvider<DeleteTransaksiNotifier, DeleteTransaksiState>(
(ref) => DeleteTransaksiNotifier(ref: ref),
);

View File

@@ -0,0 +1,79 @@
import 'package:app_petty_cash/model/history_total_model.dart';
import 'package:app_petty_cash/provider/current_info_account_balance_provider.dart';
import 'package:app_petty_cash/repository/home_repository.dart';
import 'package:app_petty_cash/repository/transaksi_repository.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../model/info_account_balance.dart';
import '../../provider/dio_provider.dart';
import '../../repository/base_repository.dart';
abstract class HistoryTotalState extends Equatable {
final DateTime date;
const HistoryTotalState(this.date);
@override
List<Object?> get props => [date];
}
class HistoryTotalStateInit extends HistoryTotalState {
HistoryTotalStateInit() : super(DateTime.now());
}
class HistoryTotalStateLoading extends HistoryTotalState {
HistoryTotalStateLoading() : super(DateTime.now());
}
class HistoryTotalStateError extends HistoryTotalState {
final String message;
HistoryTotalStateError({
required this.message,
}) : super(DateTime.now());
}
class HistoryTotalStateDone extends HistoryTotalState {
final HistoryTotalModel model;
// final String resp;
HistoryTotalStateDone({
required this.model,
}) : super(DateTime.now());
}
//notifier
class HistoryTotalNotifier extends StateNotifier<HistoryTotalState> {
final Ref ref;
HistoryTotalNotifier({
required this.ref,
}) : super(HistoryTotalStateInit());
void historyTotal(
String companyid,
String tglAwal,
String tglAkhir,
String categoryid,
) async {
try {
state = HistoryTotalStateLoading();
final dio = ref.read(dioProvider);
final resp = await TransaksiRepository(dio: dio).getHistoryTotal(
companyid,
tglAwal,
tglAkhir,
categoryid
);
state = HistoryTotalStateDone(model: resp);
} catch (e) {
if (e is BaseRepositoryException) {
state = HistoryTotalStateError(message: e.message.toString());
} else {
state = HistoryTotalStateError(message: e.toString());
}
}
}
}
//provider
final historyTotalProvider =
StateNotifierProvider<HistoryTotalNotifier, HistoryTotalState>(
(ref) => HistoryTotalNotifier(ref: ref),
);

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,3 @@
import 'package:equatable/equatable.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
@@ -44,15 +43,18 @@ class InsertTransaksiNotifier extends StateNotifier<InsertTransaksiState> {
}) : super(InsertTransaksiStateInit());
void insertTransaksi(
String tanggal,
String tipe,
String kategoriid,
String jumlah,
String catatan,
String userid,
String sender,
String url,
) async {
{required String tanggal,
required String tipe,
required String kategoriid,
required String jumlah,
required String catatan,
required String userid,
required String sender,
required String url,
required String base64file,
required String fileName,
required String fileSize,
required String fileExtension}) async {
try {
state = InsertTransaksiStateLoading();
final dio = ref.read(dioProvider);
@@ -64,6 +66,10 @@ class InsertTransaksiNotifier extends StateNotifier<InsertTransaksiState> {
catatan,
userid,
sender,
base64file,
fileName,
fileSize,
fileExtension,
url,
);
state = InsertTransaksiStateDone(resp: resp);

View File

@@ -0,0 +1,85 @@
import 'package:equatable/equatable.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../model/history_transaksi_model.dart';
import '../../repository/transaksi_repository.dart';
import '../../provider/dio_provider.dart';
import '../../repository/base_repository.dart';
import 'history_total_provider.dart';
abstract class SearchHistoryTransaksiState extends Equatable {
final DateTime date;
const SearchHistoryTransaksiState(this.date);
@override
List<Object?> get props => [date];
}
class SearchHistoryTransaksiStateInit extends SearchHistoryTransaksiState {
SearchHistoryTransaksiStateInit() : super(DateTime.now());
}
class SearchHistoryTransaksiStateLoading extends SearchHistoryTransaksiState {
SearchHistoryTransaksiStateLoading() : super(DateTime.now());
}
class SearchHistoryTransaksiStateError extends SearchHistoryTransaksiState {
final String message;
SearchHistoryTransaksiStateError({
required this.message,
}) : super(DateTime.now());
}
class SearchHistoryTransaksiStateDone extends SearchHistoryTransaksiState {
final List<HistoryTransaksiModel> model;
SearchHistoryTransaksiStateDone({
required this.model,
}) : super(DateTime.now());
}
//notifier
class SearchHistoryTransaksiNotifier
extends StateNotifier<SearchHistoryTransaksiState> {
final Ref ref;
SearchHistoryTransaksiNotifier({
required this.ref,
}) : super(SearchHistoryTransaksiStateInit());
void searchHistoryTransaksi(
String companyid,
String tglAwal,
String tglAkhir,
String categoryid,
) async {
try {
state = SearchHistoryTransaksiStateLoading();
final dio = ref.read(dioProvider);
final resp = await TransaksiRepository(dio: dio).getListHistoryTransaksi(
companyid,
tglAwal,
tglAkhir,
categoryid,
);
state = SearchHistoryTransaksiStateDone(model: resp);
// panggil provider total transaksi by category for non all
ref.read(historyTotalProvider.notifier).historyTotal(
companyid,
tglAwal,
tglAkhir,
categoryid,
);
} catch (e) {
if (e is BaseRepositoryException) {
state = SearchHistoryTransaksiStateError(message: e.message.toString());
} else {
state = SearchHistoryTransaksiStateError(message: e.toString());
}
}
}
}
//provider
final searchHistoryTransaksiProvider = StateNotifierProvider<
SearchHistoryTransaksiNotifier, SearchHistoryTransaksiState>(
(ref) => SearchHistoryTransaksiNotifier(ref: ref),
);

View File

@@ -0,0 +1,104 @@
import 'package:app_petty_cash/app/constant.dart';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
class TransaksiPickDateWidget extends StatelessWidget {
const TransaksiPickDateWidget(
{super.key,
required this.ctrlTglAwal,
required this.tglAwal,
required this.tglAwalTmp,
required this.isLoading});
final TextEditingController ctrlTglAwal;
final ValueNotifier<DateTime> tglAwal;
final ValueNotifier<String> tglAwalTmp;
final ValueNotifier<bool> isLoading;
@override
Widget build(BuildContext context) {
return Row(
children: [
Expanded(
child: TextField(
readOnly: true,
controller: ctrlTglAwal,
decoration: InputDecoration(
hintStyle: Constant.body2_400(context: context).copyWith(
color: Constant.textGreyv2,
),
labelStyle: Constant.body2_400(context: context).copyWith(
color: Constant.textGreyv2,
),
border: OutlineInputBorder(
borderSide: BorderSide(
color: Colors.orange,
width: 1,
),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Constant.textGreyv2,
width: 1,
),
),
// labelText: "Tanggal Awal",
hintText: 'Tanggal Transaksi',
// suffixIcon: isLoadingFilterScope.value
// ? SizedBox(
// width: Constant.getActualXPhone(
// context: context,
// x: 4,
// ),
// height: Constant.getActualYPhone(
// context: context,
// y: 4,
// ),
// child: CircularProgressIndicator(
// color: Constant.textRed,
// ),
// )
// : Icon(
// Icons.calendar_month_sharp,
// color: Constant.colorIconDate,
// ),
),
onTap: !isLoading.value
? () async {
final selectedDateAwal = await showDatePicker(
keyboardType: TextInputType.none,
// locale: const Locale("en-CA"),
// locale: ,
context: context,
initialEntryMode: DatePickerEntryMode.calendarOnly,
firstDate: DateTime(2000),
lastDate: DateTime(2100),
initialDate: (ctrlTglAwal.text.isEmpty)
? DateTime.now()
: tglAwal.value,
);
if (selectedDateAwal != null) {
String formattedDate =
DateFormat('dd-MM-yyyy').format(selectedDateAwal);
// ctrlTglAwal.text =
// selectedDateAwal.toString().split(' ')[0];
ctrlTglAwal.text = formattedDate;
tglAwal.value = selectedDateAwal;
tglAwalTmp.value = selectedDateAwal.toString();
}
if (selectedDateAwal == null) {
print('cancel button');
return;
}
}
: null,
),
),
],
);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,122 @@
import 'package:app_petty_cash/app/constant.dart';
import 'package:app_petty_cash/model/list_category_model.dart';
import 'package:dropdown_button2/dropdown_button2.dart';
import 'package:flutter/material.dart';
class TransaksiSelectKategoriWidget extends StatelessWidget {
const TransaksiSelectKategoriWidget({
super.key,
required this.listCategoryData,
required this.selectedListCategory,
required this.isLoading,
});
final ValueNotifier<List<ListCategory>> listCategoryData;
final ValueNotifier<ListCategory> selectedListCategory;
final ValueNotifier<bool> isLoading;
@override
Widget build(BuildContext context) {
return SizedBox(
width: Constant.getActualXPhone(context: context, x: 390),
child: DropdownButtonHideUnderline(
child: DropdownButton2<ListCategory>(
isExpanded: true,
hint: Row(
children: [
Expanded(
child: Text(
'Select Item',
style: Constant.body1(context: context).copyWith(
fontWeight: FontWeight.w400, color: Constant.textBlack),
overflow: TextOverflow.ellipsis,
),
),
],
),
items: listCategoryData.value.map((ListCategory option) {
return DropdownMenuItem<ListCategory>(
value: option,
child: Text(
option.categoryname ?? "",
overflow: TextOverflow.ellipsis,
style: Constant.body1(context: context).copyWith(
color: Constant.textBlack, fontWeight: FontWeight.w400),
),
);
}).toList(),
style: Constant.body1(context: context)
.copyWith(color: Constant.textBlack, fontWeight: FontWeight.w400),
value: selectedListCategory.value,
onChanged: !isLoading.value
? (ListCategory? newValue) {
// if (newValue) {
selectedListCategory.value = newValue!;
print(selectedListCategory.value.categoryid);
// }
}
: null,
buttonStyleData: ButtonStyleData(
height: Constant.getActualY(context: context, y: 80),
width: Constant.getActualX(context: context, x: 320),
padding: EdgeInsets.only(
left: Constant.getActualX(context: context, x: 20),
right: Constant.getActualX(context: context, x: 20),
),
decoration: BoxDecoration(
color: Constant.white,
border: Border.all(color: Constant.textGrey, width: 1),
borderRadius: BorderRadius.circular(8),
),
// elevation: 2,
),
iconStyleData: IconStyleData(
icon: Icon(
Icons.keyboard_arrow_down_outlined,
),
iconSize: 24,
iconEnabledColor: Constant.textBlack,
iconDisabledColor: Colors.grey,
),
dropdownStyleData: DropdownStyleData(
maxHeight: Constant.getActualY(context: context, y: 200),
// width: Constant.getActualX(context: context, x: 320),
padding: EdgeInsets.only(
top: Constant.getActualY(context: context, y: 10),
left: Constant.getActualX(context: context, x: 20),
right: Constant.getActualX(context: context, x: 20),
bottom: Constant.getActualY(context: context, y: 10),
),
decoration: BoxDecoration(
color: Constant.white,
borderRadius: BorderRadius.circular(8),
boxShadow: [
BoxShadow(
color: Colors.grey,
blurRadius: 20.0,
spreadRadius: 2.0,
offset: Offset(0.0, 0.0),
),
],
),
elevation: 8,
offset: const Offset(0, -10),
scrollbarTheme: ScrollbarThemeData(
radius: const Radius.circular(40),
thickness: MaterialStateProperty.all<double>(6),
thumbVisibility: MaterialStateProperty.all<bool>(true),
),
),
menuItemStyleData: MenuItemStyleData(
height: Constant.getActualY(context: context, y: 56),
padding: EdgeInsets.only(
top: Constant.getActualY(context: context, y: 10),
left: Constant.getActualX(context: context, x: 20),
right: Constant.getActualX(context: context, x: 20),
),
),
),
),
);
}
}

View File

@@ -0,0 +1,167 @@
import 'dart:io';
import 'package:app_petty_cash/app/constant.dart';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:mime/mime.dart';
class TransaksiUploadAreaWidget extends StatelessWidget {
const TransaksiUploadAreaWidget({
super.key,
required this.isImage,
required this.fileData,
required this.fileDataBase64,
required this.pickFile,
required this.pickImage,
required this.isLoading,
});
final ValueNotifier<bool> isImage;
final ValueNotifier<bool> isLoading;
final ValueNotifier<XFile?> fileData;
final ValueNotifier<String> fileDataBase64;
final Function pickImage;
final Function pickFile;
@override
Widget build(BuildContext context) {
return InkWell(
onTap: !isLoading.value
? () {
showModalBottomSheet(
context: context,
builder: (context) {
return Container(
height: Constant.getActualY(context: context, y: 200),
padding: EdgeInsets.all(20),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Column(
children: [
IconButton(
onPressed: () {
Navigator.pop(context);
pickFile();
},
icon: Icon(
Icons.folder_copy_rounded,
size: 50,
color: Constant.pcBtnBackgroundColor,
)),
Text("Browse a file",
style: Constant.body1(context: context)
.copyWith(
fontWeight: FontWeight.w600,
color: Constant.textBlack))
],
),
Column(
children: [
IconButton(
onPressed: () {
Navigator.pop(context);
pickImage();
},
icon: Icon(
Icons.add_a_photo_rounded,
size: 50,
color: Constant.pcBtnBackgroundColor,
)),
Text("Take a picture",
style: Constant.body1(context: context)
.copyWith(
fontWeight: FontWeight.w600,
color: Constant.textBlack))
],
),
],
),
);
},
);
}
: null,
child: Stack(
alignment: AlignmentDirectional.topEnd,
children: [
Container(
width: Constant.getActualXPhone(context: context, x: 390),
height: Constant.getActualYPhone(
context: context, y: isImage.value ? 200 : 83),
decoration: BoxDecoration(color: Constant.bgUploadFile),
child: Builder(builder: (context) {
final String? mime = lookupMimeType(fileData.value?.path ?? "");
return Semantics(
label: 'image_picker_example_picked_image',
child: (mime != null
? (mime.startsWith('image/'))
? Image.file(
// image: AssetImage(photo.value!.path),
File(fileData.value!.path),
frameBuilder: (context, child, frame,
wasSynchronouslyLoaded) {
return (wasSynchronouslyLoaded)
? Center(
child: Text("Loadinga"),
)
: Container(
child: child,
);
},
errorBuilder: (BuildContext context, Object error,
StackTrace? stackTrace) {
return const Center(
child: Text(
'This image type is not supported'));
},
)
: Container(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.file_present_rounded,
size: 40,
color: Constant.pcBtnBackgroundColor,
),
Text(
fileData.value?.name ?? '',
style: Constant.body2_400(context: context),
)
],
),
)
: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Icon(
Icons.upload_outlined,
color: Constant.pcBtnBackgroundColor,
),
Text(
'Upload File',
style: Constant.body1(context: context).copyWith(
fontWeight: FontWeight.w600,
color: Constant.pcBtnBackgroundColor),
)
],
)));
}),
),
if (fileData.value != null && !isLoading.value)
IconButton(
onPressed: () {
fileData.value = null;
fileDataBase64.value = '';
isImage.value = false;
},
icon: Icon(Icons.cancel_outlined)),
],
),
);
}
}

View File

@@ -32,7 +32,14 @@ class UserScreen extends HookConsumerWidget {
),
child: Scaffold(
appBar: AppBar(
title: Text('User'),
title: Text(
'User',
style: TextStyle(color: Constant.textWhite),
),
backgroundColor: Constant.pcBtnBackgroundColor,
iconTheme: IconThemeData(
color: Constant.textWhite,
),
),
drawer: CustomDrawer(),
body: SafeArea(

View File

@@ -1,4 +1,5 @@
import 'dart:async';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
@@ -7,13 +8,16 @@ import 'package:shared_preferences/shared_preferences.dart';
import '../app/constant.dart';
import '../app/route.dart';
import '../provider/current_menu_provider.dart';
import '../provider/current_user_provider.dart';
import '../screen/login/logout_provider.dart';
class CustomDrawer extends HookConsumerWidget {
// final String userCompany;
const CustomDrawer({
super.key,
});
Key? key,
// required this.userCompany,
}) : super(key: key);
@override
Widget build(BuildContext context, WidgetRef ref) {
@@ -21,6 +25,26 @@ class CustomDrawer extends HookConsumerWidget {
final isLoading = useState(false);
final errorMessage = useState("");
final successMessage = useState("");
final M_CompanyName = useState("-");
Future<String> getCompanyName() async {
final shared = await SharedPreferences.getInstance();
String M_CompanyName = "-";
if (shared != null) {
final bearerString = shared.get(Constant.bearerName).toString();
final xmodel = jsonDecode(bearerString);
if (xmodel != null) {
M_CompanyName = xmodel["model"]["M_CompanyName"];
}
}
if (M_CompanyName == "0") {
// throw BaseRepositoryException(message: 'Invalid Company ID');
}
return M_CompanyName;
}
useEffect(() {
WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
@@ -33,6 +57,7 @@ class CustomDrawer extends HookConsumerWidget {
// Navigator.popAndPushNamed(context, loginRoute);
return;
}
M_CompanyName.value = await getCompanyName();
});
return () {};
}, []);
@@ -55,7 +80,6 @@ class CustomDrawer extends HookConsumerWidget {
shared.remove(bearerString);
shared.clear();
// Navigator.popAndPushNamed(context, loginRoute);
Navigator.of(context)
.pushNamedAndRemoveUntil(loginRoute, (route) => false);
}
@@ -64,66 +88,252 @@ class CustomDrawer extends HookConsumerWidget {
});
}
});
return Drawer(
child: ListView(
padding: EdgeInsets.only(
top: Constant.getActualYPhone(context: context, y: 10),
final currentMenu = ref.read(currentPageProvider);
return Container(
width: Constant.getActualXPhone(context: context, x: 300),
height: Constant.getActualYPhone(context: context, y: 844),
child: Drawer(
elevation: 0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(0))),
child: Column(
children: [
Expanded(
child: ListView(
children: [
Container(
child: Image(
image:
AssetImage('images/logo_sismedika_landscape.png')),
),
SizedBox(
height: Constant.getActualYPhone(context: context, y: 8),
),
Padding(
padding: EdgeInsets.only(
top: Constant.getActualYPhone(context: context, y: 10),
bottom: Constant.getActualYPhone(context: context, y: 10),
right: Constant.getActualXPhone(context: context, x: 24),
left: Constant.getActualXPhone(context: context, x: 24),
),
child: Container(
width: Constant.getActualXPhone(context: context, x: 300),
),
),
// child: Padding(
// padding: EdgeInsets.only(
// left: Constant.getActualXPhone(context: context, x: 24),
// right: Constant.getActualXPhone(context: context, x: 71),
// ),
// child: Container(
// decoration: BoxDecoration(
// borderRadius: BorderRadius.circular(6),
// color: Constant.textGrey.withOpacity(0.16),
// ),
// child: Padding(
// padding: EdgeInsets.only(
// left: Constant.getActualXPhone(context: context, x: 8),
// right: Constant.getActualXPhone(context: context, x: 8),
// ),
// child: Text(
// M_CompanyName.value,
// style: Constant.body3(context: context).copyWith(
// fontWeight: FontWeight.w700,
// color: Constant.textGreyv2,
// backgroundColor: Constant.textGrey.withOpacity(0.16),
// ),
// ),
// ),
// ),
// ),
// ),
Chip(
backgroundColor: Constant.textGrey.withOpacity(0.16),
label: Text(
M_CompanyName.value,
overflow: TextOverflow.ellipsis,
style: TextStyle(
color: Constant.textGreyv2,
),
),
),
ListTile(
leading: Icon(
Icons.home,
color: (currentMenu == 0)
? Constant.pcBtnBackgroundColor
: Constant.textGreyv2,
),
title: Text(
'Home',
style: TextStyle(
color: (currentMenu == 0)
? Constant.pcBtnBackgroundColor
: Constant.textGreyv2,
),
),
onTap: () {
// Handle navigation to Home screen
Navigator.pop(context);
ref.read(currentPageProvider.state).update((state) => 0);
Navigator.pushNamed(context, homeRoute);
},
),
ListTile(
leading: Icon(
Icons.money,
color: (currentMenu == 1)
? Constant.pcBtnBackgroundColor
: Constant.textGreyv2,
),
title: Text(
'Transaksi',
style: TextStyle(
color: (currentMenu == 1)
? Constant.pcBtnBackgroundColor
: Constant.textGreyv2,
),
),
onTap: () {
// Handle navigation to Transaksi screen
Navigator.pop(context);
ref.read(currentPageProvider.state).update((state) => 1);
Navigator.pushNamed(context, transaksiRoute);
},
),
ListTile(
leading: Icon(
Icons.history,
color: (currentMenu == 2)
? Constant.pcBtnBackgroundColor
: Constant.textGreyv2,
),
title: Text(
'History Transaksi',
style: TextStyle(
color: (currentMenu == 2)
? Constant.pcBtnBackgroundColor
: Constant.textGreyv2,
),
),
onTap: () {
// Handle navigation to User screen
Navigator.pop(context);
ref.read(currentPageProvider.state).update((state) => 2);
Navigator.pushNamed(context, historyTransaksiRoute);
},
),
ListTile(
leading: Icon(
Icons.feed,
color: (currentMenu == 3)
? Constant.pcBtnBackgroundColor
: Constant.textGreyv2,
),
title: Text(
'Report',
style: TextStyle(
color: (currentMenu == 3)
? Constant.pcBtnBackgroundColor
: Constant.textGreyv2,
),
),
onTap: () {
// Handle navigation to Transaksi screen
Navigator.pop(context);
ref.read(currentPageProvider.state).update((state) => 3);
Navigator.pushNamed(context, reportRoute);
},
),
ListTile(
leading: Icon(
Icons.account_balance,
color: (currentMenu == 4)
? Constant.pcBtnBackgroundColor
: Constant.textGreyv2,
),
title: Text(
'Change Company',
style: TextStyle(
color: (currentMenu == 4)
? Constant.pcBtnBackgroundColor
: Constant.textGreyv2,
),
),
onTap: () {
// Handle navigation to Transaksi screen
Navigator.pop(context);
ref.read(currentPageProvider.state).update((state) => 4);
Navigator.pushNamed(context, changeCompanyRoute);
},
),
ListTile(
leading: Icon(
Icons.lock_reset,
color: (currentMenu == 5)
? Constant.pcBtnBackgroundColor
: Constant.textGreyv2,
),
title: Text(
'Change Password',
style: TextStyle(
color: (currentMenu == 5)
? Constant.pcBtnBackgroundColor
: Constant.textGreyv2,
),
),
onTap: () {
// Handle navigation to Transaksi screen
Navigator.pop(context);
ref.read(currentPageProvider.state).update((state) => 5);
Navigator.pushNamed(context, changePasswordRoute);
},
),
ListTile(
leading: Icon(
Icons.logout,
color: Constant.textGreyv2,
),
title: Text(
'Logout',
style: TextStyle(color: Constant.textGreyv2),
),
onTap: () {
// di set ke 0 lagi
ref.read(currentPageProvider.state).update((state) => 0);
ref.read(logoutProvider.notifier).logout(
M_UserID: selectedUser?.model.M_UserID ?? "",
M_UserUsername:
selectedUser?.model.M_UserUsername ?? "",
);
},
),
],
),
),
// Versi Aplikasi
Padding(
padding: EdgeInsets.only(
right: Constant.getActualXPhone(context: context, x: 20),
bottom: Constant.getActualYPhone(context: context, y: 10),
),
child: Align(
alignment: Alignment.bottomRight,
child: Text(
'Versi ${Constant.version}',
style: Constant.titleH4Login(context: context)
.copyWith(color: Constant.textGrey),
),
),
),
],
),
children: [
// DrawerHeader(
// decoration: BoxDecoration(
// color: Colors.blue,
// ),
// child: Text(
// 'Drawer Header',
// style: TextStyle(
// color: Colors.white,
// fontSize: 24,
// ),
// ),
// ),
ListTile(
title: Text('Home'),
onTap: () {
// Handle navigation to Home screen
Navigator.pop(context);
Navigator.pushNamed(context, homeRoute);
},
),
ListTile(
title: Text('Transaksi'),
onTap: () {
// Handle navigation to Transaksi screen
Navigator.pop(context);
Navigator.pushNamed(context, transaksiRoute);
},
),
ListTile(
title: Text('Report'),
onTap: () {
// Handle navigation to Transaksi screen
Navigator.pop(context);
Navigator.pushNamed(context, reportRoute);
},
),
ListTile(
title: Text('User'),
onTap: () {
// Handle navigation to User screen
Navigator.pop(context);
Navigator.pushNamed(context, userRoute);
},
),
ListTile(
title: Text('Logout'),
onTap: () {
ref.read(logoutProvider.notifier).logout(
M_UserID: selectedUser?.model.M_UserID ?? "",
M_UserUsername: selectedUser?.model.M_UserUsername ?? "",
);
},
),
],
),
);
}

View File

@@ -0,0 +1,221 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../app/constant.dart';
import '../app/route.dart';
import '../provider/current_menu_provider.dart';
import '../provider/current_user_provider.dart';
import '../screen/login/logout_provider.dart';
class CustomDrawerV1 extends HookConsumerWidget {
const CustomDrawerV1({
super.key,
});
@override
Widget build(BuildContext context, WidgetRef ref) {
final selectedUser = ref.read(currentUserProvider);
final isLoading = useState(false);
final errorMessage = useState("");
final successMessage = useState("");
useEffect(() {
WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
final userID = ref.read(currentUserProvider)?.model.M_UserID ?? "0";
if (userID == "0") {
//not login
Navigator.of(context)
.pushNamedAndRemoveUntil(loginRoute, (route) => true);
// Navigator.popAndPushNamed(context, loginRoute);
return;
}
});
return () {};
}, []);
ref.listen(logoutProvider, (prev, next) async {
if (next is LogoutStateLoading) {
isLoading.value = true;
} else if (next is LogoutStateError) {
isLoading.value = false;
errorMessage.value = next.message;
Timer(const Duration(seconds: 3), () {
errorMessage.value = "";
});
} else if (next is LogoutStateDone) {
isLoading.value = false;
final shared = await SharedPreferences.getInstance();
final bearerString = shared.get(Constant.bearerName).toString();
// print(bearerString);
if (bearerString.isNotEmpty) {
shared.remove(bearerString);
shared.clear();
// Navigator.popAndPushNamed(context, loginRoute);
Navigator.of(context)
.pushNamedAndRemoveUntil(loginRoute, (route) => false);
}
Timer(const Duration(seconds: 3), () async {
successMessage.value = "";
});
}
});
final currentMenu = ref.read(currentPageProvider);
return Drawer(
child: ListView(
// padding: EdgeInsets.only(
// top: Constant.getActualYPhone(context: context, y: 10),
// ),
children: [
DrawerHeader(
decoration: BoxDecoration(
color: Colors.blue,
),
child: Text(
'Drawer Header',
style: TextStyle(
color: Colors.white,
fontSize: 24,
),
),
),
ListTile(
title: Text(
'Home',
style: TextStyle(
color: (currentMenu == 0)
? Constant.textWhite
: Constant.textBlack,
),
),
tileColor: (currentMenu == 0)
? Constant.pcBtnBackgroundColor
: Colors.transparent,
onTap: () {
// Handle navigation to Home screen
Navigator.pop(context);
ref.read(currentPageProvider.state).update((state) => 0);
Navigator.pushNamed(context, homeRoute);
},
),
ListTile(
title: Text(
'Transaksi',
style: TextStyle(
color: (currentMenu == 1)
? Constant.textWhite
: Constant.textBlack,
),
),
tileColor: (currentMenu == 1)
? Constant.pcBtnBackgroundColor
: Colors.transparent,
onTap: () {
// Handle navigation to Transaksi screen
Navigator.pop(context);
ref.read(currentPageProvider.state).update((state) => 1);
Navigator.pushNamed(context, transaksiRoute);
},
),
ListTile(
title: Text(
'History Transaksi',
style: TextStyle(
color: (currentMenu == 2)
? Constant.textWhite
: Constant.textBlack,
),
),
tileColor: (currentMenu == 2)
? Constant.pcBtnBackgroundColor
: Colors.transparent,
onTap: () {
// Handle navigation to User screen
Navigator.pop(context);
ref.read(currentPageProvider.state).update((state) => 2);
Navigator.pushNamed(context, historyTransaksiRoute);
},
),
ListTile(
title: Text(
'Report',
style: TextStyle(
color: (currentMenu == 3)
? Constant.textWhite
: Constant.textBlack,
),
),
tileColor: (currentMenu == 3)
? Constant.pcBtnBackgroundColor
: Colors.transparent,
onTap: () {
// Handle navigation to Transaksi screen
Navigator.pop(context);
ref.read(currentPageProvider.state).update((state) => 3);
Navigator.pushNamed(context, reportRoute);
},
),
// ListTile(
// title: Text(
// 'User',
// style: TextStyle(
// color: (currentMenu == 4)
// ? Constant.textWhite
// : Constant.textBlack,
// ),
// ),
// tileColor: (currentMenu == 4)
// ? Constant.pcBtnBackgroundColor
// : Colors.transparent,
// onTap: () {
// // Handle navigation to User screen
// Navigator.pop(context);
// ref.read(currentPageProvider.state).update((state) => 4);
// Navigator.pushNamed(context, userRoute);
// },
// ),
// ListTile(
// title: Text(
// 'Change Company',
// style: TextStyle(
// color: (currentMenu == 5)
// ? Constant.textWhite
// : Constant.textBlack,
// ),
// ),
// tileColor: (currentMenu == 5)
// ? Constant.pcBtnBackgroundColor
// : Colors.transparent,
// onTap: () {
// // Handle navigation to User screen
// Navigator.pop(context);
// ref.read(currentPageProvider.state).update((state) => 5);
// Navigator.pushNamed(context, changeCompanyRoute);
// },
// ),
ListTile(
title: Text(
'Logout',
// style: TextStyle(color: Constant.textWhite),
),
onTap: () {
// di set ke 0 lagi
ref.read(currentPageProvider.state).update((state) => 0);
ref.read(logoutProvider.notifier).logout(
M_UserID: selectedUser?.model.M_UserID ?? "",
M_UserUsername: selectedUser?.model.M_UserUsername ?? "",
);
},
),
],
),
);
}
}

View File

@@ -0,0 +1,341 @@
import 'dart:async';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../app/constant.dart';
import '../app/route.dart';
import '../provider/current_menu_provider.dart';
import '../provider/current_user_provider.dart';
import '../screen/login/logout_provider.dart';
class CustomDrawerV2 extends HookConsumerWidget {
// final String userCompany;
const CustomDrawerV2({
Key? key,
// required this.userCompany,
}) : super(key: key);
@override
Widget build(BuildContext context, WidgetRef ref) {
final selectedUser = ref.read(currentUserProvider);
final isLoading = useState(false);
final errorMessage = useState("");
final successMessage = useState("");
final M_CompanyName = useState("-");
Future<String> getCompanyName() async {
final shared = await SharedPreferences.getInstance();
String M_CompanyName = "-";
if (shared != null) {
final bearerString = shared.get(Constant.bearerName).toString();
final xmodel = jsonDecode(bearerString);
if (xmodel != null) {
M_CompanyName = xmodel["model"]["M_CompanyName"];
}
}
if (M_CompanyName == "0") {
// throw BaseRepositoryException(message: 'Invalid Company ID');
}
return M_CompanyName;
}
useEffect(() {
WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
final userID = ref.read(currentUserProvider)?.model.M_UserID ?? "0";
if (userID == "0") {
//not login
Navigator.of(context)
.pushNamedAndRemoveUntil(loginRoute, (route) => true);
// Navigator.popAndPushNamed(context, loginRoute);
return;
}
M_CompanyName.value = await getCompanyName();
});
return () {};
}, []);
ref.listen(logoutProvider, (prev, next) async {
if (next is LogoutStateLoading) {
isLoading.value = true;
} else if (next is LogoutStateError) {
isLoading.value = false;
errorMessage.value = next.message;
Timer(const Duration(seconds: 3), () {
errorMessage.value = "";
});
} else if (next is LogoutStateDone) {
isLoading.value = false;
final shared = await SharedPreferences.getInstance();
final bearerString = shared.get(Constant.bearerName).toString();
// print(bearerString);
if (bearerString.isNotEmpty) {
shared.remove(bearerString);
shared.clear();
// Navigator.popAndPushNamed(context, loginRoute);
Navigator.of(context)
.pushNamedAndRemoveUntil(loginRoute, (route) => false);
}
Timer(const Duration(seconds: 3), () async {
successMessage.value = "";
});
}
});
final currentMenu = ref.read(currentPageProvider);
return Drawer(
elevation: 0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(0))),
child: Container(
child: ListView(
// padding: EdgeInsets.only(
// top: Constant.getActualYPhone(context: context, y: 10),
// ),
children: [
Container(
// decoration: BoxDecoration(
// border: Border(bottom: BorderSide(color: Colors.transparent)) // <--- use the global theme to change the dividerColor property
// ),
child: Image(
image: AssetImage('images/logo_sismedika_landscape.png')),
),
SizedBox(
height: Constant.getActualYPhone(context: context, y: 8),
),
Padding(
padding: EdgeInsets.only(
top: Constant.getActualYPhone(context: context, y: 10),
bottom: Constant.getActualYPhone(context: context, y: 10),
right: Constant.getActualXPhone(context: context, x: 24),
left: Constant.getActualXPhone(context: context, x: 24),
),
child: Container(
width: Constant.getActualXPhone(context: context, x: 300),
),
),
// Expanded(
// child: Padding(
// padding: EdgeInsets.only(
// left: Constant.getActualXPhone(context: context, x: 24),
// right: Constant.getActualXPhone(context: context, x: 71),
// ),
// child: Container(
// decoration: BoxDecoration(
// borderRadius: BorderRadius.circular(6),
// color: Constant.textGrey.withOpacity(0.16),
// ),
// child: Padding(
// padding: EdgeInsets.only(
// left: Constant.getActualXPhone(context: context, x: 8),
// right: Constant.getActualXPhone(context: context, x: 8),
// ),
// child: Text(
// M_CompanyName.value,
// style: Constant.body3(context: context).copyWith(
// fontWeight: FontWeight.w700,
// color: Constant.textGreyv2,
// backgroundColor: Constant.textGrey.withOpacity(0.16),
// ),
// ),
// ),
// ),
// ),
// ),
Chip(
backgroundColor: Constant.textGrey.withOpacity(0.16),
label: Text(
M_CompanyName.value,
overflow: TextOverflow.ellipsis,
style: TextStyle(
color: Constant.textGreyv2,
),
),
),
ListTile(
leading: Icon(
Icons.home,
color: (currentMenu == 0)
? Constant.pcBtnBackgroundColor
: Constant.textGreyv2,
),
title: Text(
'Home',
style: TextStyle(
color: (currentMenu == 0)
? Constant.pcBtnBackgroundColor
: Constant.textGreyv2,
),
),
// tileColor: (currentMenu == 0)
// ? Constant.pcBtnBackgroundColor
// : Colors.transparent,
onTap: () {
// Handle navigation to Home screen
Navigator.pop(context);
ref.read(currentPageProvider.state).update((state) => 0);
Navigator.pushNamed(context, homeRoute);
},
),
ListTile(
leading: Icon(
Icons.money,
color: (currentMenu == 1)
? Constant.pcBtnBackgroundColor
: Constant.textGreyv2,
),
title: Text(
'Transaksi',
style: TextStyle(
color: (currentMenu == 1)
? Constant.pcBtnBackgroundColor
: Constant.textGreyv2,
),
),
// tileColor: (currentMenu == 1)
// ? Constant.pcBtnBackgroundColor
// : Colors.transparent,
onTap: () {
// Handle navigation to Transaksi screen
Navigator.pop(context);
ref.read(currentPageProvider.state).update((state) => 1);
Navigator.pushNamed(context, transaksiRoute);
},
),
ListTile(
leading: Icon(
Icons.history,
color: (currentMenu == 2)
? Constant.pcBtnBackgroundColor
: Constant.textGreyv2,
),
title: Text(
'History Transaksi',
style: TextStyle(
color: (currentMenu == 2)
? Constant.pcBtnBackgroundColor
: Constant.textGreyv2,
),
),
// tileColor: (currentMenu == 2)
// ? Constant.pcBtnBackgroundColor
// : Colors.transparent,
onTap: () {
// Handle navigation to User screen
Navigator.pop(context);
ref.read(currentPageProvider.state).update((state) => 2);
Navigator.pushNamed(context, historyTransaksiRoute);
},
),
ListTile(
leading: Icon(
Icons.feed,
color: (currentMenu == 3)
? Constant.pcBtnBackgroundColor
: Constant.textGreyv2,
),
title: Text(
'Report',
style: TextStyle(
color: (currentMenu == 3)
? Constant.pcBtnBackgroundColor
: Constant.textGreyv2,
),
),
// tileColor: (currentMenu == 3)
// ? Constant.pcBtnBackgroundColor
// : Colors.transparent,
onTap: () {
// Handle navigation to Transaksi screen
Navigator.pop(context);
ref.read(currentPageProvider.state).update((state) => 3);
Navigator.pushNamed(context, reportRoute);
},
),
// ListTile(
// title: Text(
// 'User',
// style: TextStyle(
// color: (currentMenu == 4)
// ? Constant.textWhite
// : Constant.textBlack,
// ),
// ),
// tileColor: (currentMenu == 4)
// ? Constant.pcBtnBackgroundColor
// : Colors.transparent,
// onTap: () {
// // Handle navigation to User screen
// Navigator.pop(context);
// ref.read(currentPageProvider.state).update((state) => 4);
// Navigator.pushNamed(context, userRoute);
// },
// ),
// ListTile(
// title: Text(
// 'Change Company',
// style: TextStyle(
// color: (currentMenu == 5)
// ? Constant.textWhite
// : Constant.textBlack,
// ),
// ),
// tileColor: (currentMenu == 5)
// ? Constant.pcBtnBackgroundColor
// : Colors.transparent,
// onTap: () {
// // Handle navigation to User screen
// Navigator.pop(context);
// ref.read(currentPageProvider.state).update((state) => 5);
// Navigator.pushNamed(context, changeCompanyRoute);
// },
// ),
ListTile(
leading: Icon(
Icons.logout,
color: Constant.textGreyv2,
),
title: Text(
'Logout',
style: TextStyle(color: Constant.textGreyv2),
),
onTap: () {
// di set ke 0 lagi
ref.read(currentPageProvider.state).update((state) => 0);
ref.read(logoutProvider.notifier).logout(
M_UserID: selectedUser?.model.M_UserID ?? "",
M_UserUsername: selectedUser?.model.M_UserUsername ?? "",
);
},
),
Spacer(),
// Versi Aplikasi
ListTile(
title: Text(
'Versi ${Constant.version}',
style: Constant.titleH4Login(context: context)
.copyWith(color: Constant.textGrey),
),
onTap: () {},
),
],
),
),
);
}
}

View File

@@ -0,0 +1,42 @@
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import '../app/constant.dart';
class FieldRowHistoryTransaksi extends HookConsumerWidget {
final String label;
final String value;
const FieldRowHistoryTransaksi({
Key? key,
required this.label,
required this.value,
}) : super(key: key);
@override
Widget build(BuildContext context, WidgetRef ref) {
return Row(
children: [
Padding(
padding: EdgeInsets.only(left: 10),
child: SizedBox(
width: Constant.getActualXPhone(context: context, x: 100),
child: Text(label),
),
),
Padding(
padding: EdgeInsets.symmetric(horizontal: 10),
child: Text(':'),
),
Expanded(
child: Padding(
padding: EdgeInsets.only(right: 10),
child: Text(
value,
style: TextStyle(fontWeight: FontWeight.bold),
),
),
),
],
);
}
}

View File

@@ -0,0 +1,300 @@
import 'package:app_petty_cash/widget/sankbar_widget.dart';
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:intl/intl.dart';
import '../app/constant.dart';
class HistoryRowAtasWidget extends HookConsumerWidget {
final String icon_category_id;
final String icon_category_name;
final String amount;
final String tipe;
final String tglTransaksi;
// final ValueNotifier<bool> searchHistoryIsLoading;
const HistoryRowAtasWidget({
Key? key,
required this.icon_category_id,
required this.icon_category_name,
required this.amount,
required this.tipe,
required this.tglTransaksi,
// required this.searchHistoryIsLoading,
}) : super(key: key);
@override
Widget build(BuildContext context, WidgetRef ref) {
// check file exist
// Future<Widget> checkFileExistence(String fileUrl) async {
// try {
// final dio = Dio();
// final response = await dio.head(fileUrl);
// if (response.statusCode == 200) {
// return SvgPicture.network(
// fileUrl,
// semanticsLabel: 'Icon pizza',
// // placeholderBuilder: (BuildContext context) => Container(
// // padding: const EdgeInsets.all(30.0),
// // child: const CircularProgressIndicator(),
// // ),
// );
// } else {
// return SvgPicture.network(
// 'https://devone.aplikasi.web.id/pettycash-media/icon/icon_13.svg',
// semanticsLabel: 'Icon pizza',
// // placeholderBuilder: (BuildContext context) => Container(
// // padding: const EdgeInsets.all(30.0),
// // child: const CircularProgressIndicator(),
// // ),
// );
// }
// } catch (e) {
// // print('Error checking file existence: $e');
// return SvgPicture.network(
// 'https://devone.aplikasi.web.id/pettycash-media/icon/icon_13.svg',
// semanticsLabel: 'Icon pizza',
// // placeholderBuilder: (BuildContext context) => Container(
// // padding: const EdgeInsets.all(30.0),
// // child: const CircularProgressIndicator(),
// // ),
// );
// }
// }
final iconProvider =
FutureProvider.family<Widget, String>((ref, fileUrl) async {
try {
final dio = Dio();
final response = await dio.head(fileUrl);
if (response.statusCode == 200) {
return SvgPicture.network(
fileUrl,
semanticsLabel: 'Icon pizza',
);
} else {
return SvgPicture.network(
'https://devone.aplikasi.web.id/pettycash-media/icon/icon_13.svg',
semanticsLabel: 'Icon pizza',
);
}
} catch (e) {
return SvgPicture.network(
'https://devone.aplikasi.web.id/pettycash-media/icon/icon_13.svg',
semanticsLabel: 'Icon pizza',
);
}
});
// fungsi tanggal
String formatDateString(String inputDate) {
try {
// Parsing tanggal dari string input
DateTime date = DateFormat('dd-MM-yyyy', 'id').parse(inputDate);
// Format tanggal ke '30 Des 2023'
String formattedDate = DateFormat('dd MMM yyyy', 'id').format(date);
return formattedDate;
} catch (e) {
// Tangkap kesalahan jika format tanggal tidak sesuai
print('Error parsing date: $e');
// return 'Format Tanggal Salah';
return inputDate;
}
}
Widget getIconUrl(String categoryid) {
List<Map<String, String>> iconDataList = [
{
'categoryid': '1',
'url':
'https://devone.aplikasi.web.id/pettycash-media/icon/icon_13.svg'
},
{
'categoryid': '2',
'url':
'https://devone.aplikasi.web.id/pettycash-media/icon/icon_13.svg'
},
{
'categoryid': '3',
'url':
'https://devone.aplikasi.web.id/pettycash-media/icon/icon_3.svg'
},
{
'categoryid': '4',
'url':
'https://devone.aplikasi.web.id/pettycash-media/icon/icon_13.svg'
},
{
'categoryid': '5',
'url':
'https://devone.aplikasi.web.id/pettycash-media/icon/icon_5.svg'
},
{
'categoryid': '6',
'url':
'https://devone.aplikasi.web.id/pettycash-media/icon/icon_6.svg'
},
{
'categoryid': '7',
'url':
'https://devone.aplikasi.web.id/pettycash-media/icon/icon_13.svg'
},
{
'categoryid': '8',
'url':
'https://devone.aplikasi.web.id/pettycash-media/icon/icon_13.svg'
},
{
'categoryid': '9',
'url':
'https://devone.aplikasi.web.id/pettycash-media/icon/icon_13.svg'
},
{
'categoryid': '10',
'url':
'https://devone.aplikasi.web.id/pettycash-media/icon/icon_13.svg'
},
{
'categoryid': '11',
'url':
'https://devone.aplikasi.web.id/pettycash-media/icon/icon_13.svg'
},
{
'categoryid': '12',
'url':
'https://devone.aplikasi.web.id/pettycash-media/icon/icon_12.svg'
},
{
'categoryid': '13',
'url':
'https://devone.aplikasi.web.id/pettycash-media/icon/icon_13.svg'
},
];
String urlFix = "";
for (var i = 0; i < iconDataList.length; i++) {
if (iconDataList[i]['categoryid'] == categoryid) {
// urlFix =
// "https://devone.aplikasi.web.id/pettycash-media/icon/icon_$icon_category_id.svg";
urlFix = iconDataList[i]['url'] ??
"https://devone.aplikasi.web.id/pettycash-media/icon/icon_13.svg";
}
}
if (urlFix == "") {
urlFix =
"https://devone.aplikasi.web.id/pettycash-media/icon/icon_13.svg";
}
// return urlFix;
return SvgPicture.network(
urlFix,
semanticsLabel: 'Icon category',
);
}
return Row(
children: [
// kiri
Container(
width: Constant.getActualXPhone(context: context, x: 38),
height: Constant.getActualYPhone(context: context, y: 38),
decoration: BoxDecoration(
color: Color.fromRGBO(241, 90, 41, 0.08),
borderRadius: BorderRadius.all(
Radius.circular(8.0),
),
),
child: getIconUrl(icon_category_id),
),
// tengah
Expanded(
child: Padding(
padding: EdgeInsets.only(
left: Constant.getActualYPhone(context: context, y: 20),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
Text(
icon_category_name,
style: Constant.body2_600(context: context).copyWith(
fontWeight: FontWeight.w600,
color: Constant.textBlack,
),
),
SizedBox(
height: Constant.getActualYPhone(
context: context,
y: 8,
),
),
Text(
amount,
style: Constant.body1_600(context: context).copyWith(
fontWeight: FontWeight.w600,
color: Constant.pcBtnBackgroundColor),
),
],
),
),
),
// kanan
Expanded(
child: Padding(
padding: EdgeInsets.only(
right: Constant.getActualYPhone(context: context, y: 8),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisAlignment: MainAxisAlignment.end,
children: [
Container(
width: Constant.getActualXPhone(context: context, x: 80),
height: Constant.getActualYPhone(context: context, y: 24),
decoration: BoxDecoration(
color: Constant.bgIconHistory,
borderRadius: BorderRadius.all(
Radius.circular(6.0),
),
),
child: Align(
alignment: Alignment.center,
child: Text(
tipe,
style: Constant.body1_600(context: context).copyWith(
fontWeight: FontWeight.w600,
color: Constant.pcBtnBackgroundColor,
fontSize: 14,
),
),
),
),
SizedBox(
height: Constant.getActualYPhone(
context: context,
y: 8,
),
),
Text(
formatDateString(tglTransaksi),
style: Constant.body1_600(context: context).copyWith(
fontWeight: FontWeight.w600, color: Constant.textGreyv2),
),
],
),
),
),
],
);
}
}

View File

@@ -6,9 +6,13 @@
#include "generated_plugin_registrant.h"
#include <file_selector_linux/file_selector_plugin.h>
#include <url_launcher_linux/url_launcher_plugin.h>
void fl_register_plugins(FlPluginRegistry* registry) {
g_autoptr(FlPluginRegistrar) file_selector_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "FileSelectorPlugin");
file_selector_plugin_register_with_registrar(file_selector_linux_registrar);
g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin");
url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar);

View File

@@ -3,6 +3,7 @@
#
list(APPEND FLUTTER_PLUGIN_LIST
file_selector_linux
url_launcher_linux
)

View File

@@ -5,10 +5,14 @@
import FlutterMacOS
import Foundation
import file_selector_macos
import path_provider_foundation
import shared_preferences_foundation
import url_launcher_macos
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin"))
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
}

View File

@@ -1,68 +1,68 @@
{
"images" : [
{
"size" : "16x16",
"idiom" : "mac",
"filename" : "app_icon_16.png",
"scale" : "1x"
"info": {
"version": 1,
"author": "xcode"
},
{
"size" : "16x16",
"idiom" : "mac",
"filename" : "app_icon_32.png",
"scale" : "2x"
},
{
"size" : "32x32",
"idiom" : "mac",
"filename" : "app_icon_32.png",
"scale" : "1x"
},
{
"size" : "32x32",
"idiom" : "mac",
"filename" : "app_icon_64.png",
"scale" : "2x"
},
{
"size" : "128x128",
"idiom" : "mac",
"filename" : "app_icon_128.png",
"scale" : "1x"
},
{
"size" : "128x128",
"idiom" : "mac",
"filename" : "app_icon_256.png",
"scale" : "2x"
},
{
"size" : "256x256",
"idiom" : "mac",
"filename" : "app_icon_256.png",
"scale" : "1x"
},
{
"size" : "256x256",
"idiom" : "mac",
"filename" : "app_icon_512.png",
"scale" : "2x"
},
{
"size" : "512x512",
"idiom" : "mac",
"filename" : "app_icon_512.png",
"scale" : "1x"
},
{
"size" : "512x512",
"idiom" : "mac",
"filename" : "app_icon_1024.png",
"scale" : "2x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
"images": [
{
"size": "16x16",
"idiom": "mac",
"filename": "app_icon_16.png",
"scale": "1x"
},
{
"size": "16x16",
"idiom": "mac",
"filename": "app_icon_32.png",
"scale": "2x"
},
{
"size": "32x32",
"idiom": "mac",
"filename": "app_icon_32.png",
"scale": "1x"
},
{
"size": "32x32",
"idiom": "mac",
"filename": "app_icon_64.png",
"scale": "2x"
},
{
"size": "128x128",
"idiom": "mac",
"filename": "app_icon_128.png",
"scale": "1x"
},
{
"size": "128x128",
"idiom": "mac",
"filename": "app_icon_256.png",
"scale": "2x"
},
{
"size": "256x256",
"idiom": "mac",
"filename": "app_icon_256.png",
"scale": "1x"
},
{
"size": "256x256",
"idiom": "mac",
"filename": "app_icon_512.png",
"scale": "2x"
},
{
"size": "512x512",
"idiom": "mac",
"filename": "app_icon_512.png",
"scale": "1x"
},
{
"size": "512x512",
"idiom": "mac",
"filename": "app_icon_1024.png",
"scale": "2x"
}
]
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 101 KiB

After

Width:  |  Height:  |  Size: 167 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 520 B

After

Width:  |  Height:  |  Size: 715 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 4.0 KiB

Some files were not shown because too many files have changed in this diff Show More