diff --git a/app_petty_cash/images/icon_12.png b/app_petty_cash/images/icon_12.png new file mode 100644 index 0000000..5395e37 Binary files /dev/null and b/app_petty_cash/images/icon_12.png differ diff --git a/app_petty_cash/images/icon_12.svg b/app_petty_cash/images/icon_12.svg new file mode 100644 index 0000000..1ed2c00 --- /dev/null +++ b/app_petty_cash/images/icon_12.svg @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app_petty_cash/images/icon_13.svg b/app_petty_cash/images/icon_13.svg new file mode 100644 index 0000000..b540a9b --- /dev/null +++ b/app_petty_cash/images/icon_13.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/app_petty_cash/images/icon_3.svg b/app_petty_cash/images/icon_3.svg new file mode 100644 index 0000000..4695dfd --- /dev/null +++ b/app_petty_cash/images/icon_3.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/app_petty_cash/images/icon_4.png b/app_petty_cash/images/icon_4.png new file mode 100644 index 0000000..7859bb9 Binary files /dev/null and b/app_petty_cash/images/icon_4.png differ diff --git a/app_petty_cash/images/icon_5.png b/app_petty_cash/images/icon_5.png new file mode 100644 index 0000000..ef0e085 Binary files /dev/null and b/app_petty_cash/images/icon_5.png differ diff --git a/app_petty_cash/images/icon_5.svg b/app_petty_cash/images/icon_5.svg new file mode 100644 index 0000000..a38d0a3 --- /dev/null +++ b/app_petty_cash/images/icon_5.svg @@ -0,0 +1,4 @@ + + + + diff --git a/app_petty_cash/images/icon_6.png b/app_petty_cash/images/icon_6.png new file mode 100644 index 0000000..85ba30e Binary files /dev/null and b/app_petty_cash/images/icon_6.png differ diff --git a/app_petty_cash/images/icon_6.svg b/app_petty_cash/images/icon_6.svg new file mode 100644 index 0000000..2a77e56 --- /dev/null +++ b/app_petty_cash/images/icon_6.svg @@ -0,0 +1,3 @@ + + + diff --git a/app_petty_cash/images/icon_delete.png b/app_petty_cash/images/icon_delete.png new file mode 100644 index 0000000..e11e1cc Binary files /dev/null and b/app_petty_cash/images/icon_delete.png differ diff --git a/app_petty_cash/lib/app/constant.dart b/app_petty_cash/lib/app/constant.dart index 0486c28..f013b79 100644 --- a/app_petty_cash/lib/app/constant.dart +++ b/app_petty_cash/lib/app/constant.dart @@ -15,8 +15,7 @@ class Constant { // static String baseUrl = "https://devregonline.pramita.co.id/one-api/xdoc/"; // tester devbandungraya - static String baseUrlDevoneReport = - "devone.aplikasi.web.id"; + static String baseUrlDevoneReport = "devone.aplikasi.web.id"; static String baseUrlDevone = "http://devone.aplikasi.web.id/one-api-pettycash/pettycash/"; diff --git a/app_petty_cash/lib/model/history_transaksi_model.dart b/app_petty_cash/lib/model/history_transaksi_model.dart new file mode 100644 index 0000000..6d944f3 --- /dev/null +++ b/app_petty_cash/lib/model/history_transaksi_model.dart @@ -0,0 +1,64 @@ +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; + + 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}); + + HistoryTransaksiModel.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/repository/transaksi_repository.dart b/app_petty_cash/lib/repository/transaksi_repository.dart index 9c8fdc4..77e0674 100644 --- a/app_petty_cash/lib/repository/transaksi_repository.dart +++ b/app_petty_cash/lib/repository/transaksi_repository.dart @@ -1,6 +1,7 @@ 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'; @@ -93,4 +94,31 @@ class TransaksiRepository extends BaseRepository { return resp['status']; } + + // list history transaksi + Future> getListHistoryTransaksi( + String companyid, + String tglAwal, + String tglAkhir, + String categoryid, + ) async { + final service = + "${Constant.baseUrlDevone}/homescreen/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.empty(growable: true); + resp['data'].forEach((e) { + final model = HistoryTransaksiModel.fromJson(e); + result.add(model); + }); + return result; + } } diff --git a/app_petty_cash/lib/screen/transaksi/history_transaksi_screen.dart b/app_petty_cash/lib/screen/transaksi/history_transaksi_screen.dart index 8400a6c..dcb0a82 100644 --- a/app_petty_cash/lib/screen/transaksi/history_transaksi_screen.dart +++ b/app_petty_cash/lib/screen/transaksi/history_transaksi_screen.dart @@ -1,18 +1,162 @@ +import 'dart:convert'; + +import 'package:app_petty_cash/screen/transaksi/search_history_transaksi_provider.dart'; +import 'package:dropdown_button2/dropdown_button2.dart'; import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:flutter_svg/svg.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/history_transaksi_model.dart'; +import '../../model/list_category_model.dart'; +import '../../model/list_type_model.dart'; import '../../provider/current_user_provider.dart'; import '../../widget/custom_drawer.dart'; import '../../widget/field_row_history_transaksi.dart'; +import '../../widget/history_row_atas_widget.dart'; +import '../../widget/sankbar_widget.dart'; +import 'list_category_provider.dart'; +import 'list_type_provider.dart'; class HistoryTransaksiScreen extends HookConsumerWidget { const HistoryTransaksiScreen({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { + final listTypeData = useState>(List.empty(growable: true)); + final selectedListTypeData = useState(ListType()); + + final listTypeLoading = useState(false); + + // category + final listCategoryLoading = useState(false); + final selectedListCategory = useState(ListCategory()); + final listCategoryData = useState>( + List.empty( + growable: true, + ), + ); + + String M_CompanyID = "0"; + String formattedDate = DateFormat('dd-MM-yyyy').format(DateTime.now()); + final ctrlTglAwal = useTextEditingController(text: formattedDate); + + final tglAwal = useState(DateTime.now()); + final tglAwalTmp = useState(""); + + final ctrlTglAkhir = useTextEditingController(text: formattedDate); + + final tglAkhir = useState(DateTime.now()); + final tglAkhirTmp = useState(""); + final categoryIsLoading = useState(false); + final searchHistoryIsLoading = useState(false); + final listSearchHistory = useState>( + List.empty( + growable: true, + ), + ); + + // A. LISTEN PROVIDER + + // type + ref.listen( + listTypeProvider, + (previous, next) { + if (next is ListTypeStateLoading) { + listTypeLoading.value = true; + } else if (next is ListTypeStateError) { + // print(next.message); + listTypeLoading.value = false; + SanckbarWidget(context, next.message, snackbarType.error); + } else if (next is ListTypeStateDone) { + // print(jsonEncode(next.model)); + // print(next.model.length); + listTypeData.value = next.model; + listTypeLoading.value = false; + + var idx = 0; + for (var i = 0; i < listTypeData.value.length; i++) { + if (listTypeData.value[i].typeid == "KREDIT") { + selectedListTypeData.value = listTypeData.value[i]; + idx = i; + } + } + selectedListTypeData.value = listTypeData.value[idx]; + } + }, + ); + + // category + ref.listen( + listCategoryProvider, + (previous, next) { + if (next is ListCategoryStateLoading) { + listCategoryLoading.value = true; + } else if (next is ListCategoryStateError) { + // print(next.message); + listCategoryLoading.value = false; + SanckbarWidget(context, next.message, snackbarType.error); + } else if (next is ListCategoryStateDone) { + // print(jsonEncode(next.model)); + // print(next.model.length); + // listCategoryData.value = next.model; + final xList = List.empty(growable: true); + xList.add( + ListCategory( + categoryid: "0", + categoryname: "ALL", + ), + ); + xList.addAll(next.model); + listCategoryData.value = xList; + listCategoryLoading.value = false; + selectedListCategory.value = listCategoryData.value[0]; + } + }, + ); + + // search + ref.listen( + searchHistoryTransaksiProvider, + (previous, next) { + if (next is SearchHistoryTransaksiStateLoading) { + searchHistoryIsLoading.value = true; + } else if (next is SearchHistoryTransaksiStateError) { + // print(next.message); + searchHistoryIsLoading.value = false; + SanckbarWidget(context, next.message, snackbarType.error); + } else if (next is SearchHistoryTransaksiStateDone) { + // print(jsonEncode(next.model)); + // print(next.model.length); + listSearchHistory.value = next.model; + searchHistoryIsLoading.value = false; + } + }, + ); + + 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 { final userID = ref.read(currentUserProvider)?.model.M_UserID ?? "0"; @@ -28,6 +172,38 @@ class HistoryTransaksiScreen extends HookConsumerWidget { return () {}; }, []); + useEffect(() { + WidgetsBinding.instance.addPostFrameCallback((timeStamp) async { + // list Type Provider + ref.read(listTypeProvider.notifier).getListType(); + + // list category provider + ref.read(listCategoryProvider.notifier).getListCategory(); + + M_CompanyID = await getCompanyID(); + + DateTime parsedDate = DateFormat('dd-MM-yyyy').parse( + ctrlTglAwal.value.text.toString(), + ); + String formattedDateAwal = DateFormat('yyyy-MM-dd').format(parsedDate); + + DateTime parsedDateAkhir = DateFormat('dd-MM-yyyy').parse( + ctrlTglAkhir.value.text.toString(), + ); + String formattedDateAkhir = + DateFormat('yyyy-MM-dd').format(parsedDateAkhir); + + final categoryid = selectedListCategory.value.categoryid ?? "0"; + + // search + ref + .read(searchHistoryTransaksiProvider.notifier) + .searchHistoryTransaksi( + M_CompanyID, formattedDateAwal, formattedDateAkhir, categoryid); + }); + return () {}; + }, []); + return Padding( padding: EdgeInsets.only( top: Constant.getActualYPhone(context: context, y: 30), @@ -45,100 +221,517 @@ class HistoryTransaksiScreen extends HookConsumerWidget { ), drawer: CustomDrawer(), body: SafeArea( - child: SingleChildScrollView( - child: Padding( - padding: EdgeInsets.only( - top: Constant.getActualYPhone(context: context, y: 10), - left: Constant.getActualXPhone(context: context, x: 8), - right: Constant.getActualXPhone(context: context, x: 8), - ), - child: Container( - height: MediaQuery.of(context).size.height - 10, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - // Card looping transaksi - Card( - margin: EdgeInsets.all(16.0), - child: Padding( - padding: EdgeInsets.all(16.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - FieldRowHistoryTransaksi( - label: "Tipe", - value: "DEBIT", + child: Padding( + padding: EdgeInsets.only( + top: Constant.getActualYPhone(context: context, y: 15), + left: Constant.getActualXPhone(context: context, x: 10), + right: Constant.getActualXPhone(context: context, x: 10), + bottom: Constant.getActualYPhone(context: context, y: 10), + ), + child: Container( + // height: MediaQuery.of(context).size.height - 10, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // filter + Row( + children: [ + // Tanggal Awal + Expanded( + child: TextField( + controller: ctrlTglAwal, + decoration: InputDecoration( + hintStyle: + Constant.body2_400(context: context).copyWith( + color: Constant.textGreyv2, ), - SizedBox(height: 8.0), - FieldRowHistoryTransaksi( - label: "Tanggal", - value: "15-12-2023", + labelStyle: + Constant.body2_400(context: context).copyWith( + color: Constant.textGreyv2, ), - SizedBox(height: 8.0), - FieldRowHistoryTransaksi( - label: "Nominal", - value: "100.000", - ), - SizedBox(height: 8.0), - FieldRowHistoryTransaksi( - label: "Kategori", - value: "Jumat Sehat", - ), - SizedBox(height: 8.0), - Chip( - label: Text( - 'Verify', - style: TextStyle(color: Constant.green_600), + border: OutlineInputBorder( + borderSide: BorderSide( + color: Colors.orange, + width: 1, ), - shape: RoundedRectangleBorder( - side: BorderSide( - color: Constant.green_600, - ), - borderRadius: BorderRadius.circular(4.0), + ), + focusedBorder: OutlineInputBorder( + borderSide: BorderSide( + color: Constant.textGreyv2, + width: 1, ), - backgroundColor: Colors.white, ), - SizedBox( - height: Constant.getActualYPhone( - context: context, y: 6), - ), - Chip( - label: Text( - 'Not Verify', - style: TextStyle(color: Constant.textGreyv2), - ), - shape: RoundedRectangleBorder( - side: BorderSide( - color: Constant.textGreyv2, - ), - borderRadius: BorderRadius.circular(4.0), - ), - backgroundColor: Colors.white, - ), - Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - ElevatedButton( - onPressed: () { - // Handle delete button tap - }, - child: Text( - 'Delete', - style: TextStyle(color: Constant.textWhite), - ), - style: ElevatedButton.styleFrom( - backgroundColor: Colors - .red, // Ganti dengan warna delete button yang diinginkan - ), - ), - ], - ), - ], + 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( + width: + Constant.getActualXPhone(context: context, x: 10), + ), + // Tanggal Akhir + Expanded( + child: TextField( + controller: ctrlTglAkhir, + 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 Akhir", + 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; + } + }, + ), + ), + ], + ), + SizedBox( + height: Constant.getActualYPhone(context: context, y: 20), + ), + Text( + 'Kategori', + style: Constant.body1(context: context).copyWith( + fontWeight: FontWeight.w600, + color: Constant.textBlack, ), - ], - ), + ), + SizedBox( + height: Constant.getActualYPhone(context: context, y: 10), + ), + // Dropdown kategori + (listCategoryLoading.value) + ? SizedBox( + width: + Constant.getActualXPhone(context: context, x: 24), + height: + Constant.getActualYPhone(context: context, y: 32), + child: Center( + child: CircularProgressIndicator(), + ), + ) + : SizedBox( + width: Constant.getActualXPhone( + context: context, x: 390), + child: DropdownButtonHideUnderline( + child: DropdownButton2( + isExpanded: true, + hint: Row( + children: [ + Expanded( + child: Text( + 'Select Item', + style: Constant.body1(context: context) + .copyWith( + fontWeight: FontWeight.w600, + color: Constant.textBlack), + overflow: TextOverflow.ellipsis, + ), + ), + ], + ), + items: listCategoryData.value + .map((ListCategory option) { + return DropdownMenuItem( + value: option, + child: Text( + option.categoryname ?? "", + style: Constant.body1(context: context) + .copyWith( + color: Constant.textBlack, + fontWeight: FontWeight.w600), + ), + ); + }).toList(), + value: selectedListCategory.value, + onChanged: (ListCategory? newValue) { + // if (newValue) { + selectedListCategory.value = newValue!; + print(selectedListCategory.value.categoryid); + // } + }, + buttonStyleData: ButtonStyleData( + height: Constant.getActualY( + context: context, y: 56), + width: Constant.getActualX( + context: context, x: 320), + padding: EdgeInsets.only( + left: Constant.getActualX( + context: context, x: 10), + right: Constant.getActualX( + context: context, x: 10), + ), + decoration: BoxDecoration( + color: Constant.white, + border: Border.all( + color: Constant.textBlack, 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: 10), + right: Constant.getActualX( + context: context, x: 10), + 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(6), + thumbVisibility: + MaterialStateProperty.all(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: 10), + right: Constant.getActualX( + context: context, x: 10), + ), + ), + ), + ), + ), + + SizedBox( + height: Constant.getActualYPhone(context: context, y: 10), + ), + Container( + width: Constant.getActualXPhone(context: context, x: 390), + child: Align( + alignment: Alignment.centerRight, + child: ElevatedButton( + style: ButtonStyle( + backgroundColor: MaterialStateColor.resolveWith( + (st) => Constant.pcBtnBackgroundColor), + shape: + MaterialStateProperty.all( + RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + side: BorderSide( + color: Constant.pcBtnBackgroundColor, + ), + ), + ), + shadowColor: + MaterialStateProperty.all(Color(0xffff48423d)), + elevation: MaterialStateProperty.all(4.0), + ), + child: Stack( + children: [ + (categoryIsLoading.value) + ? SizedBox( + width: Constant.getActualXPhone( + context: context, x: 24), + height: Constant.getActualYPhone( + context: context, y: 32), + child: CircularProgressIndicator( + color: Colors.white, + ), + ) + : Text( + 'Search', + style: Constant.body1(context: context) + .copyWith( + fontWeight: FontWeight.w600, + color: Constant.white), + ), + ], + ), + onPressed: () async { + M_CompanyID = await getCompanyID(); + ref + .read(searchHistoryTransaksiProvider.notifier) + .searchHistoryTransaksi( + M_CompanyID, + ctrlTglAwal.text, + ctrlTglAkhir.text, + selectedListCategory.value.categoryid ?? "0", + ); + }, + ), + ), + ), + + SizedBox( + height: Constant.getActualYPhone(context: context, y: 20), + ), + + // Card looping transaksi + + // (searchHistoryIsLoading.value) + // ? Center( + // child: CircularProgressIndicator(), + // ) + // : Expanded( + // child: ListView.builder( + // itemCount: listSearchHistory.value.length, + // itemBuilder: (context, idx) { + // return Card( + // child: Padding( + // padding: EdgeInsets.all(16.0), + // child: Column( + // crossAxisAlignment: + // CrossAxisAlignment.start, + // children: [ + // FieldRowHistoryTransaksi( + // label: "Tipe", + // value: listSearchHistory + // .value[idx].tipe + // .toString(), + // ), + // SizedBox(height: 8.0), + // FieldRowHistoryTransaksi( + // label: "Tanggal", + // value: listSearchHistory + // .value[idx].tanggaltransaksi + // .toString(), + // ), + // SizedBox(height: 8.0), + // FieldRowHistoryTransaksi( + // label: "Nominal", + // value: listSearchHistory + // .value[idx].amount + // .toString(), + // // value: "nunggu api", + // ), + // SizedBox(height: 8.0), + // FieldRowHistoryTransaksi( + // label: "Kategori", + // value: listSearchHistory + // .value[idx].kategoriname + // .toString(), + // ), + // SizedBox(height: 8.0), + // if (listSearchHistory + // .value[idx].isconfirm == + // "Y") + // Row( + // mainAxisAlignment: + // MainAxisAlignment.end, + // children: [ + // Chip( + // label: Text( + // 'Confirmed', + // style: TextStyle( + // color: + // Constant.green_600), + // ), + // shape: RoundedRectangleBorder( + // side: BorderSide( + // color: + // Constant.green_600), + // borderRadius: + // BorderRadius.circular( + // 4.0), + // ), + // backgroundColor: Colors.white, + // ), + // ], + // ), + // SizedBox( + // height: Constant.getActualYPhone( + // context: context, y: 6), + // ), + // if (listSearchHistory + // .value[idx].isconfirm == + // "N") + // Row( + // mainAxisAlignment: + // MainAxisAlignment.end, + // children: [ + // ElevatedButton( + // onPressed: () { + // // Handle delete button tap + // }, + // child: Text( + // 'Delete', + // style: TextStyle( + // color: + // Constant.textWhite), + // ), + // style: ElevatedButton.styleFrom( + // backgroundColor: Colors + // .red, // Ganti dengan warna delete button yang diinginkan + // ), + // ), + // ], + // ) + // ], + // ), + // ), + // ); + // }, + // ), + // ), + + Card( + elevation: 2.0, + child: Padding( + padding: const EdgeInsets.all(10), + child: Column( + // crossAxisAlignment: CrossAxisAlignment.start, + // mainAxisAlignment: MainAxisAlignment.start, + children: [ + // atas + HistoryRowAtasWidget(), + + // bawah + Row(), + ], + ), + ), + ), + ], ), ), ), diff --git a/app_petty_cash/lib/screen/transaksi/search_history_transaksi_provider.dart b/app_petty_cash/lib/screen/transaksi/search_history_transaksi_provider.dart new file mode 100644 index 0000000..d434b69 --- /dev/null +++ b/app_petty_cash/lib/screen/transaksi/search_history_transaksi_provider.dart @@ -0,0 +1,76 @@ +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'; + +abstract class SearchHistoryTransaksiState extends Equatable { + final DateTime date; + const SearchHistoryTransaksiState(this.date); + @override + List 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 model; + SearchHistoryTransaksiStateDone({ + required this.model, + }) : super(DateTime.now()); +} + +//notifier +class SearchHistoryTransaksiNotifier + extends StateNotifier { + 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); + } 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), +); diff --git a/app_petty_cash/lib/widget/custom_drawer.dart b/app_petty_cash/lib/widget/custom_drawer.dart index 203a50e..d1e2ae6 100644 --- a/app_petty_cash/lib/widget/custom_drawer.dart +++ b/app_petty_cash/lib/widget/custom_drawer.dart @@ -162,44 +162,44 @@ class CustomDrawer extends HookConsumerWidget { 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( + // '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', diff --git a/app_petty_cash/lib/widget/history_row_atas_widget.dart b/app_petty_cash/lib/widget/history_row_atas_widget.dart new file mode 100644 index 0000000..42b3faa --- /dev/null +++ b/app_petty_cash/lib/widget/history_row_atas_widget.dart @@ -0,0 +1,110 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; + +import '../app/constant.dart'; + +class HistoryRowAtasWidget extends HookConsumerWidget { + const HistoryRowAtasWidget({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + 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: Text('s'), + child: SvgPicture.network( + 'https://devone.aplikasi.web.id/pettycash-media/icon/icon_12.svg', + semanticsLabel: 'Icon pizza', + placeholderBuilder: (BuildContext context) => Container( + padding: const EdgeInsets.all(30.0), + child: const CircularProgressIndicator(), + ), + ), + ), + + // tengah + Expanded( + child: Padding( + padding: EdgeInsets.only(left: 20), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Text( + 'Makanan & Minuman', + style: Constant.body2_600(context: context).copyWith( + fontWeight: FontWeight.w600, color: Constant.textBlack), + ), + SizedBox( + height: Constant.getActualYPhone( + context: context, + y: 8, + ), + ), + Text( + '100000', + style: Constant.body1_600(context: context).copyWith( + fontWeight: FontWeight.w600, + color: Constant.pcBtnBackgroundColor), + ), + ], + ), + ), + ), + + // kanan + Expanded( + child: Padding( + padding: EdgeInsets.only(right: 8), + child: Column( + crossAxisAlignment: CrossAxisAlignment.end, + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Container( + // width: Constant.getActualXPhone(context: context, x: 32), + height: Constant.getActualYPhone(context: context, y: 19), + decoration: BoxDecoration( + color: Color.fromRGBO(241, 90, 41, 0.16), + borderRadius: BorderRadius.all( + Radius.circular(6.0), + ), + ), + child: Padding( + padding: EdgeInsets.all(2.0), + child: Text( + 'Kredit', + style: Constant.body1_600(context: context).copyWith( + fontWeight: FontWeight.w600, + color: Constant.pcBtnBackgroundColor), + ), + ), + ), + SizedBox( + height: Constant.getActualYPhone( + context: context, + y: 8, + ), + ), + Text( + '15 Des 2023', + style: Constant.body1_600(context: context).copyWith( + fontWeight: FontWeight.w600, color: Constant.textGreyv2), + ), + ], + ), + ), + ), + ], + ); + } +} diff --git a/app_petty_cash/pubspec.lock b/app_petty_cash/pubspec.lock index ba1a4c2..d9ea55c 100644 --- a/app_petty_cash/pubspec.lock +++ b/app_petty_cash/pubspec.lock @@ -1,6 +1,14 @@ # Generated by pub # See https://dart.dev/tools/pub/glossary#lockfile packages: + args: + dependency: transitive + description: + name: args + sha256: eef6c46b622e0494a36c5a12d10d77fb4e855501a91c1b9ef9339326e58f0596 + url: "https://pub.dev" + source: hosted + version: "2.4.2" async: dependency: transitive description: @@ -190,6 +198,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.4" + flutter_svg: + dependency: "direct main" + description: + name: flutter_svg + sha256: d39e7f95621fc84376bc0f7d504f05c3a41488c562f4a8ad410569127507402c + url: "https://pub.dev" + source: hosted + version: "2.0.9" flutter_test: dependency: "direct dev" description: flutter @@ -280,6 +296,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.8.3" + path_parsing: + dependency: transitive + description: + name: path_parsing + sha256: e3e67b1629e6f7e8100b367d3db6ba6af4b1f0bb80f64db18ef1fbabd2fa9ccf + url: "https://pub.dev" + source: hosted + version: "1.0.1" path_provider_linux: dependency: transitive description: @@ -344,6 +368,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.1.3" + petitparser: + dependency: transitive + description: + name: petitparser + sha256: cb3798bef7fc021ac45b308f4b51208a152792445cce0448c9a4ba5879dd8750 + url: "https://pub.dev" + source: hosted + version: "5.4.0" platform: dependency: transitive description: @@ -565,6 +597,30 @@ packages: url: "https://pub.dev" source: hosted version: "3.1.1" + vector_graphics: + dependency: transitive + description: + name: vector_graphics + sha256: "18f6690295af52d081f6808f2f7c69f0eed6d7e23a71539d75f4aeb8f0062172" + url: "https://pub.dev" + source: hosted + version: "1.1.9+2" + vector_graphics_codec: + dependency: transitive + description: + name: vector_graphics_codec + sha256: "531d20465c10dfac7f5cd90b60bbe4dd9921f1ec4ca54c83ebb176dbacb7bb2d" + url: "https://pub.dev" + source: hosted + version: "1.1.9+2" + vector_graphics_compiler: + dependency: transitive + description: + name: vector_graphics_compiler + sha256: "03012b0a33775c5530576b70240308080e1d5050f0faf000118c20e6463bc0ad" + url: "https://pub.dev" + source: hosted + version: "1.1.9+2" vector_math: dependency: transitive description: @@ -589,6 +645,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.4" + xml: + dependency: transitive + description: + name: xml + sha256: "5bc72e1e45e941d825fd7468b9b4cc3b9327942649aeb6fc5cdbf135f0a86e84" + url: "https://pub.dev" + source: hosted + version: "6.3.0" sdks: dart: ">=3.0.6 <4.0.0" flutter: ">=3.10.0" diff --git a/app_petty_cash/pubspec.yaml b/app_petty_cash/pubspec.yaml index 93edb0c..1c82ca6 100644 --- a/app_petty_cash/pubspec.yaml +++ b/app_petty_cash/pubspec.yaml @@ -50,6 +50,7 @@ dependencies: flutter_multi_formatter: ^2.12.4 top_snackbar_flutter: ^3.1.0 url_launcher: ^6.1.13 + flutter_svg: ^2.0.9 dev_dependencies: flutter_test: