diff --git a/app_petty_cash/images/background_card.png b/app_petty_cash/images/background_card.png new file mode 100644 index 0000000..0b209a6 Binary files /dev/null and b/app_petty_cash/images/background_card.png differ diff --git a/app_petty_cash/lib/app/constant.dart b/app_petty_cash/lib/app/constant.dart index 1cf9e35..0bbacad 100644 --- a/app_petty_cash/lib/app/constant.dart +++ b/app_petty_cash/lib/app/constant.dart @@ -282,6 +282,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), @@ -353,6 +360,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), diff --git a/app_petty_cash/lib/model/info_account_balance.dart b/app_petty_cash/lib/model/info_account_balance.dart new file mode 100644 index 0000000..91f261f --- /dev/null +++ b/app_petty_cash/lib/model/info_account_balance.dart @@ -0,0 +1,21 @@ +class InfoAccountBalanceModel { + String? totalAll; + String? kredit; + String? debit; + + InfoAccountBalanceModel({this.totalAll, this.kredit, this.debit}); + + InfoAccountBalanceModel.fromJson(Map json) { + totalAll = json['total_all']; + kredit = json['kredit']; + debit = json['debit']; + } + + Map toJson() { + final Map data = new Map(); + data['total_all'] = this.totalAll; + data['kredit'] = this.kredit; + data['debit'] = this.debit; + return data; + } +} \ No newline at end of file diff --git a/app_petty_cash/lib/model/list_transaksi_model.dart b/app_petty_cash/lib/model/list_transaksi_model.dart new file mode 100644 index 0000000..aa964e6 --- /dev/null +++ b/app_petty_cash/lib/model/list_transaksi_model.dart @@ -0,0 +1,64 @@ +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; + + 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}); + + ListTransaksiHomeModel.fromJson(Map 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']; + } + + Map toJson() { + final Map data = new Map(); + 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; + return data; + } +} \ No newline at end of file diff --git a/app_petty_cash/lib/provider/current_info_account_balance_provider.dart b/app_petty_cash/lib/provider/current_info_account_balance_provider.dart new file mode 100644 index 0000000..7f6c611 --- /dev/null +++ b/app_petty_cash/lib/provider/current_info_account_balance_provider.dart @@ -0,0 +1,11 @@ +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +import '../model/info_account_balance.dart'; + +final currentInfoAccountBalanceProvider = StateProvider( + ((ref) => InfoAccountBalanceModel( + debit: "0", + kredit: "0", + totalAll: "0", + )), +); \ No newline at end of file diff --git a/app_petty_cash/lib/repository/home_repository.dart b/app_petty_cash/lib/repository/home_repository.dart new file mode 100644 index 0000000..e2b9f17 --- /dev/null +++ b/app_petty_cash/lib/repository/home_repository.dart @@ -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> 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.empty(growable: true); + resp['data'].forEach((e) { + final model = ListTransaksiHomeModel.fromJson(e); + result.add(model); + }); + return result; + } + + // info account balance + Future 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; + } +} diff --git a/app_petty_cash/lib/screen/home/home_screen.dart b/app_petty_cash/lib/screen/home/home_screen.dart index b279317..bd8f783 100644 --- a/app_petty_cash/lib/screen/home/home_screen.dart +++ b/app_petty_cash/lib/screen/home/home_screen.dart @@ -1,17 +1,34 @@ +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: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("0"); + final searchTransaksiHomeLoading = useState(false); + final infoAccountBalanceHomeLoading = useState(false); + final listTransaksiHome = useState>( + List.empty(growable: true), + ); + useEffect(() { WidgetsBinding.instance.addPostFrameCallback((timeStamp) async { final userID = ref.read(currentUserProvider)?.model.M_UserID ?? "0"; @@ -27,6 +44,80 @@ class HomeScreen extends HookConsumerWidget { return () {}; }, []); + Future 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; + } + }, + ); + return Padding( padding: EdgeInsets.only( top: Constant.getActualYPhone(context: context, y: 30), @@ -44,8 +135,258 @@ class HomeScreen extends HookConsumerWidget { ), 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(), + ), + ], + ), + ), + ), + ); + }, + ), + ), + ), + ], + ), + ), + ), ), ), ), diff --git a/app_petty_cash/lib/screen/home/info_account_balance_provider.dart b/app_petty_cash/lib/screen/home/info_account_balance_provider.dart new file mode 100644 index 0000000..34a3b03 --- /dev/null +++ b/app_petty_cash/lib/screen/home/info_account_balance_provider.dart @@ -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 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 { + 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), +); diff --git a/app_petty_cash/lib/screen/home/list_transaksi_home_provider.dart b/app_petty_cash/lib/screen/home/list_transaksi_home_provider.dart new file mode 100644 index 0000000..1c254a9 --- /dev/null +++ b/app_petty_cash/lib/screen/home/list_transaksi_home_provider.dart @@ -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 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 model; + // final String resp; + ListTransaksiHomeStateDone({ + required this.model, + }) : super(DateTime.now()); +} + +//notifier +class ListTransaksiHomeNotifier extends StateNotifier { + 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( + (ref) => ListTransaksiHomeNotifier(ref: ref), +);