route , approval screen & approval detail

This commit is contained in:
Sas Andy
2024-02-22 10:38:38 +07:00
parent 3790e9e11a
commit eb7236671e
13 changed files with 2190 additions and 0 deletions

View File

@@ -1,3 +1,5 @@
import 'package:absensi_sas/screen/approval/approval_screen.dart';
import 'package:absensi_sas/screen/approval_detail/approval_detail_screen.dart';
import 'package:absensi_sas/screen/home/home_screen_v1.dart';
import 'package:absensi_sas/screen/presensi/presensi_screen.dart';
import 'package:absensi_sas/screen/presensi/presensi_selfie_screen.dart';
@@ -13,6 +15,8 @@ const testFlutterMapRoute = "/testFlutterMapRoute";
const homeRoute = "/homeRoute";
const presensiRoute = "/presensiRoute";
const presensiSelfieRoute = "/presensiSelfieRoute";
const approvalRoute = "/approvalRoute";
const approvalDetailRoute = "/approvalDetailRoute";
class AppRoute {
static Route<dynamic> generateRoute(RouteSettings settings) {
@@ -81,6 +85,26 @@ class AppRoute {
);
});
}
// approval
if (settings.name == approvalRoute) {
return MaterialPageRoute(builder: (context) {
return MediaQuery(
data: MediaQuery.of(context)
.copyWith(textScaleFactor: 1.0, padding: EdgeInsets.all(0)),
child: ApprovalScreen(),
);
});
}
// approvalDetail
if (settings.name == approvalDetailRoute) {
return MaterialPageRoute(builder: (context) {
return MediaQuery(
data: MediaQuery.of(context)
.copyWith(textScaleFactor: 1.0, padding: EdgeInsets.all(0)),
child: ApprovalDetailScreen(),
);
});
}
return MaterialPageRoute(builder: (context) {
return MediaQuery(

View File

@@ -0,0 +1,158 @@
import 'package:absensi_sas/model/approval_detail_model.dart';
import 'package:absensi_sas/model/approval_model.dart';
List<ApprovalModel> approvalCardList = [
ApprovalModel(
id: "1",
name: "Alfianto Andy P",
date: "21 Feb 2024",
time: "08:03",
typeID: "1",
typename: "Presensi",
presensitypeID: "1",
presensiTypeName: "Clock In",
address:
"Jl. Kwini No.1, RT.5/RW.1, Senen, Kec. Senen, Kota Jakarta Pusat, Daerah Khusus Ibukota Jakarta 10410",
statusID: "",
statusName: ""),
ApprovalModel(
id: "1",
name: "Alfianto Andy P",
date: "21 Feb 2024",
time: "08:03",
typeID: "1",
typename: "Presensi",
presensitypeID: "1",
presensiTypeName: "Clock In",
address:
"Jl. Kwini No.1, RT.5/RW.1, Senen, Kec. Senen, Kota Jakarta Pusat, Daerah Khusus Ibukota Jakarta 10410",
statusID: "1",
statusName: "Approve"),
ApprovalModel(
id: "2",
name: "Alfianto Andy P",
date: "21 Feb 2024",
time: "17:03",
typeID: "1",
typename: "Presensi",
presensitypeID: "2",
presensiTypeName: "Clock Out",
address:
"Jl. Kwini No.1, RT.5/RW.1, Senen, Kec. Senen, Kota Jakarta Pusat, Daerah Khusus Ibukota Jakarta 10410",
statusID: "2",
statusName: "Reject"),
ApprovalModel(
id: "3",
name: "Hanan Askarim",
date: "06 Jan 2024 - 06 Jan 2024",
time: "0",
typeID: "2",
typename: "Cuti",
statusID: "0",
reasonType: "Cuti Tahunan",
reasonDescription: "Kondangan",
reasontypeID: "1",
statusName: "0"),
ApprovalModel(
id: "4",
name: "Hanan Askarim",
date: "06 Jan 2024 - 06 Jan 2024",
time: "0",
typeID: "2",
typename: "Cuti",
statusID: "1",
reasonType: "Cuti Tahunan",
reasonDescription: "Tidur",
reasontypeID: "1",
statusName: "Approve"),
ApprovalModel(
id: "4",
name: "Hanan Askarim",
date: "06 Jan 2024 - 06 Jan 2024",
time: "0",
typeID: "2",
typename: "Cuti",
statusID: "2",
reasonType: "Cuti Tahunan",
reasonDescription: "Tidur",
reasontypeID: "1",
statusName: "Reject"),
ApprovalModel(
id: "5",
name: "Stephen Kusumo",
date: "06 Jan 2024",
time: "17:00 - 19:00",
typeID: "3",
typename: "Lembur",
statusID: "0",
reasonType: "0",
reasonDescription: "Menyelesaikan project Petty Cash",
reasontypeID: "0",
statusName: ""),
ApprovalModel(
id: "5",
name: "Stephen Kusumo",
date: "06 Jan 2024",
time: "17:00 - 19:00",
typeID: "3",
typename: "Lembur",
statusID: "1",
reasonType: "0",
reasonDescription: "Menyelesaikan project Petty Cash",
reasontypeID: "0",
statusName: "Approve"),
ApprovalModel(
id: "5",
name: "Stephen Kusumo",
date: "06 Jan 2024",
time: "17:00 - 19:00",
typeID: "3",
typename: "Lembur",
statusID: "2",
reasonType: "0",
reasonDescription: "Menyelesaikan project Petty Cash",
reasontypeID: "0",
statusName: "Reject"),
];
List<ApprovalDetailModel> approvalDetailMockup = [
ApprovalDetailModel(
id: "1",
name: "Alfianto Andy P",
address:
"Jl. Kwini No.1, RT.5/RW.1, Senen, Kec. Senen, Kota Jakarta Pusat, Daerah Khusus Ibukota Jakarta 10410",
date: "06 Feb 2024",
time: "08:08",
nip: "SS202308",
lat: -7.539538,
long: 110.798357,
imagePath: "https://images4.alphacoders.com/262/262196.jpg",
statusID: "1",
statusName: "Approve"),
ApprovalDetailModel(
id: "2",
name: "Alfianto Andy P",
address:
"Jl. Kwini No.1, RT.5/RW.1, Senen, Kec. Senen, Kota Jakarta Pusat, Daerah Khusus Ibukota Jakarta 10410",
date: "06 Feb 2024",
time: "08:08",
nip: "SS202308",
lat: -7.539538,
long: 110.798357,
imagePath: "https://images3.alphacoders.com/147/147465.jpg",
statusID: "2",
statusName: "Reject"),
ApprovalDetailModel(
id: "3",
name: "Alfianto Andy P",
address:
"Jl. Kwini No.1, RT.5/RW.1, Senen, Kec. Senen, Kota Jakarta Pusat, Daerah Khusus Ibukota Jakarta 10410",
date: "06 Feb 2024",
time: "08:08",
nip: "SS202308",
lat: -7.539538,
long: 110.798357,
imagePath: "https://images4.alphacoders.com/146/146466.jpg",
statusID: "0",
statusName: ""),
];

View File

@@ -0,0 +1,159 @@
import 'package:absensi_sas/app/constant.dart';
import 'package:absensi_sas/app/route.dart';
import 'package:absensi_sas/model/approval_model.dart';
import 'package:absensi_sas/provider/approval_filter_provider.dart';
import 'package:absensi_sas/screen/approval/approval_mockup.dart';
import 'package:absensi_sas/screen/approval/widget/approval_card_cuti.dart';
import 'package:absensi_sas/screen/approval/widget/approval_card_lembur.dart';
import 'package:absensi_sas/screen/approval/widget/approval_card_presensi.dart';
import 'package:absensi_sas/screen/approval/widget/approval_filter_chip_widget.dart';
import 'package:absensi_sas/screen/approval/widget/approval_filter_widget.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
class ApprovalScreen extends HookConsumerWidget {
const ApprovalScreen({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final approvalFilterTempProvider = ref.watch(approvalFilterProvider);
final approvalTypeList = approvalFilterTempProvider.approvalTypeList;
final scrollCtr = useScrollController();
final List<ApprovalModel> dataMockup = approvalCardList;
onTap(String tipeID) {
if (tipeID == "1") {
print("Tipe Id 1");
Navigator.pushNamed(context, approvalDetailRoute);
}
}
return SafeArea(
minimum: EdgeInsets.only(
top: Constant.getActualYPhone(context: context, y: 30)),
child: Scaffold(
backgroundColor: Constant.textWhite,
appBar: AppBar(
backgroundColor: Colors.white,
shadowColor: Colors.white,
surfaceTintColor: Colors.white,
scrolledUnderElevation: 0,
elevation: 0,
leading: Container(
child: IconButton(
onPressed: () {
Navigator.pop(context);
},
icon: Icon(Icons.arrow_back_ios_new_rounded)),
),
centerTitle: true,
title: Text(
"Approval",
style: Constant.title_screen(context: context),
),
),
body: Padding(
padding: EdgeInsets.symmetric(
horizontal: Constant.getActualXPhone(context: context, x: 20)),
child: Column(
children: [
Container(
color: Colors.white,
child: Row(
children: [
ApprovalFilterWidget(),
Expanded(
child: LayoutBuilder(
builder: (context, constraints) {
return Container(
width: constraints.maxWidth,
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
children: [
...approvalTypeList.map(
(e) => ApprovalFilterChipWidget(data: e))
],
),
),
);
},
),
)
],
),
),
SizedBox(
height: Constant.getActualYPhone(context: context, y: 27),
),
Expanded(child: Container(
// color: Colors.red,
child: LayoutBuilder(builder: (context, Constraints) {
return Container(
height: Constraints.maxWidth,
child: RefreshIndicator(
onRefresh: () async {
print("Refresh");
},
child: ListView(
controller: scrollCtr,
children: [
Container(
margin: EdgeInsets.only(bottom: 28),
width: MediaQuery.of(context).size.width,
child: Row(
children: [
Container(
child: Text(
"Februari 2024",
style: Constant.body_16(context: context)
.copyWith(fontWeight: FontWeight.w600),
),
),
Expanded(
child: Container(
margin: EdgeInsets.only(left: 10),
child: Divider(
height: 10,
),
),
)
],
),
),
...dataMockup.map((e) {
switch (e.typeID) {
case "1":
return ApprovalCardPresensi(
data: e,
onTap: onTap,
);
case "2":
return ApprovalCardCuti(
data: e,
);
case "3":
return ApprovalCardLembur(
data: e,
);
default:
return Container();
}
}).toList()
],
),
),
);
}),
))
],
),
),
),
);
}
}

View File

@@ -0,0 +1,200 @@
import 'package:absensi_sas/app/constant.dart';
import 'package:absensi_sas/model/approval_model.dart';
import 'package:absensi_sas/screen/approval/widget/approval_card_status_widget.dart';
import 'package:absensi_sas/screen/approval/widget/approval_dialog_confirmation.dart';
import 'package:absensi_sas/widget/custom_button_approval.dart';
import 'package:eva_icons_flutter/eva_icons_flutter.dart';
import 'package:flutter/material.dart';
import 'package:iconify_flutter/iconify_flutter.dart';
import 'package:iconify_flutter/icons/heroicons.dart';
class ApprovalCardCuti extends StatelessWidget {
const ApprovalCardCuti({super.key, required this.data});
final ApprovalModel data;
@override
Widget build(BuildContext context) {
return Container(
margin: EdgeInsets.only(
bottom: Constant.getActualYPhone(context: context, y: 20)),
child: InkWell(
customBorder: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
),
splashColor: Constant.bgOrange,
overlayColor: MaterialStatePropertyAll(Constant.bgOrange),
onTap: () {
print("tapped");
},
child: Ink(
decoration: BoxDecoration(boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.12),
offset: Offset(0.0, 10), //(x,y)
blurRadius: 4.0,
),
BoxShadow(
color: Colors.grey.withOpacity(0.2),
offset: Offset(0.0, 0), //(x,y)
blurRadius: 2.0,
),
], color: Colors.white, borderRadius: BorderRadius.circular(16)),
child: Column(
children: [
Container(
padding: EdgeInsets.symmetric(
horizontal:
Constant.getActualXPhone(context: context, x: 20),
vertical:
Constant.getActualYPhone(context: context, y: 16)),
child: Column(
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
data.name ?? "",
style: Constant.body_16(context: context),
),
Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
color: Constant.primaryOrange.withOpacity(0.2)),
padding: EdgeInsets.symmetric(
horizontal: Constant.getActualXPhone(
context: context, x: 8),
vertical: Constant.getActualYPhone(
context: context, y: 1)),
child: Text(
data.typename ?? "",
style: Constant.body_12(context: context)
.copyWith(color: Constant.primaryOrange),
),
)
],
),
SizedBox(
height:
Constant.getActualYPhone(context: context, y: 8),
),
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Icon(
EvaIcons.calendar,
color: Constant.primaryOrange,
size: 14,
),
SizedBox(
width: Constant.getActualXPhone(
context: context, x: 8),
),
Flexible(
child: Text(
data.date ?? "",
style: Constant.titleH2_400_14(context: context)
.copyWith(color: Constant.textDarkGrey),
),
)
],
),
SizedBox(
height:
Constant.getActualYPhone(context: context, y: 8),
),
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Icon(
// ,
// color: Constant.primaryOrange,
// size: 14,
// ),
Iconify(
Heroicons.chat_bubble_bottom_center_text_solid,
color: Constant.primaryOrange,
size: 14,
),
SizedBox(
width: Constant.getActualXPhone(
context: context, x: 8),
),
Flexible(
child: Text(
"${data.reasonType} ▪️ ${data.reasonDescription}",
style: Constant.titleH2_400_14(context: context)
.copyWith(color: Constant.textDarkGrey),
),
),
],
),
SizedBox(
height:
Constant.getActualYPhone(context: context, y: 20),
),
Row(
children: List.generate(
1000 ~/ 10,
(index) => Expanded(
child: Container(
color: index % 2 == 0
? Colors.transparent
: Constant.textLightGrey
.withOpacity(0.24),
height: 1,
),
)),
),
if (data.statusID != "1" && data.statusID != "2")
SizedBox(
height:
Constant.getActualYPhone(context: context, y: 20),
),
if (data.statusID != "1" && data.statusID != "2")
Row(
children: [
Expanded(
child: CustomButtonWhite(
btnText: "Reject",
onPressed: () {
ApprovalDialogConfirmation(
context: context,
confirmFunc: () {},
textInformation: "Menolak ",
staffName: data.name ?? "",
typename: data.typename ?? "");
}),
),
SizedBox(
width: Constant.getActualXPhone(
context: context, x: 24),
),
Expanded(
child: CustomButtonOrange(
btnText: "Approve",
onPressed: () {
ApprovalDialogConfirmation(
context: context,
confirmFunc: () {},
textInformation: "Menyetujui ",
staffName: data.name ?? "",
typename: data.typename ?? "");
}),
),
],
)
],
),
),
AprrovalCardStatusWidget(
statusID: data.statusID ?? "",
statusName: data.statusName ?? '',
)
],
)),
),
);
}
}

View File

@@ -0,0 +1,200 @@
import 'package:absensi_sas/app/constant.dart';
import 'package:absensi_sas/model/approval_model.dart';
import 'package:absensi_sas/screen/approval/widget/approval_card_status_widget.dart';
import 'package:absensi_sas/screen/approval/widget/approval_dialog_confirmation.dart';
import 'package:absensi_sas/widget/custom_button_approval.dart';
import 'package:eva_icons_flutter/eva_icons_flutter.dart';
import 'package:flutter/material.dart';
import 'package:iconify_flutter/iconify_flutter.dart';
import 'package:iconify_flutter/icons/heroicons.dart';
class ApprovalCardLembur extends StatelessWidget {
const ApprovalCardLembur({super.key, required this.data});
final ApprovalModel data;
@override
Widget build(BuildContext context) {
return Container(
margin: EdgeInsets.only(
bottom: Constant.getActualYPhone(context: context, y: 20)),
child: InkWell(
customBorder: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
),
splashColor: Constant.bgOrange,
overlayColor: MaterialStatePropertyAll(Constant.bgOrange),
onTap: () {
print("tapped");
},
child: Ink(
decoration: BoxDecoration(boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.12),
offset: Offset(0.0, 10), //(x,y)
blurRadius: 4.0,
),
BoxShadow(
color: Colors.grey.withOpacity(0.2),
offset: Offset(0.0, 0), //(x,y)
blurRadius: 2.0,
),
], color: Colors.white, borderRadius: BorderRadius.circular(16)),
child: Column(
children: [
Container(
padding: EdgeInsets.symmetric(
horizontal:
Constant.getActualXPhone(context: context, x: 20),
vertical:
Constant.getActualYPhone(context: context, y: 16)),
child: Column(
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
data.name ?? "",
style: Constant.body_16(context: context),
),
Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
color: Constant.bgSecondaryBlue),
padding: EdgeInsets.symmetric(
horizontal: Constant.getActualXPhone(
context: context, x: 8),
vertical: Constant.getActualYPhone(
context: context, y: 1)),
child: Text(
data.typename ?? "",
style: Constant.body_12(context: context)
.copyWith(color: Constant.secondaryBlue),
),
)
],
),
SizedBox(
height:
Constant.getActualYPhone(context: context, y: 8),
),
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Icon(
EvaIcons.calendar,
color: Constant.textOrange,
size: 14,
),
SizedBox(
width: Constant.getActualXPhone(
context: context, x: 8),
),
Flexible(
child: Text(
data.date ?? "",
style: Constant.titleH2_400_14(context: context)
.copyWith(color: Constant.textDarkGrey),
),
)
],
),
SizedBox(
height:
Constant.getActualYPhone(context: context, y: 8),
),
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Icon(
// ,
// color: Constant.textOrange,
// size: 14,
// ),
Iconify(
Heroicons.chat_bubble_bottom_center_text_solid,
color: Constant.textOrange,
size: 14,
),
SizedBox(
width: Constant.getActualXPhone(
context: context, x: 8),
),
Flexible(
child: Text(
data.reasonDescription ?? '',
style: Constant.titleH2_400_14(context: context)
.copyWith(color: Constant.textDarkGrey),
),
),
],
),
SizedBox(
height:
Constant.getActualYPhone(context: context, y: 20),
),
Row(
children: List.generate(
1000 ~/ 10,
(index) => Expanded(
child: Container(
color: index % 2 == 0
? Colors.transparent
: Constant.textLightGrey
.withOpacity(0.24),
height: 1,
),
)),
),
if (data.statusID != "1" && data.statusID != "2")
SizedBox(
height:
Constant.getActualYPhone(context: context, y: 20),
),
if (data.statusID != "1" && data.statusID != "2")
Row(
children: [
Expanded(
child: CustomButtonWhite(
btnText: "Reject",
onPressed: () {
ApprovalDialogConfirmation(
context: context,
confirmFunc: () {},
textInformation: "Menolak ",
staffName: data.name ?? "",
typename: data.typename ?? "");
}),
),
SizedBox(
width: Constant.getActualXPhone(
context: context, x: 24),
),
Expanded(
child: CustomButtonOrange(
btnText: "Approve",
onPressed: () {
ApprovalDialogConfirmation(
context: context,
confirmFunc: () {},
textInformation: "Menyetujui ",
staffName: data.name ?? "",
typename: data.typename ?? "");
}),
),
],
),
],
),
),
AprrovalCardStatusWidget(
statusID: data.statusID ?? "",
statusName: data.statusName ?? '',
)
],
)),
),
);
}
}

View File

@@ -0,0 +1,203 @@
import 'package:absensi_sas/app/constant.dart';
import 'package:absensi_sas/model/approval_model.dart';
import 'package:absensi_sas/screen/approval/widget/approval_card_status_widget.dart';
import 'package:eva_icons_flutter/eva_icons_flutter.dart';
import 'package:flutter/material.dart';
class ApprovalCardPresensi extends StatelessWidget {
const ApprovalCardPresensi(
{super.key, required this.data, required this.onTap});
final ApprovalModel data;
final Function onTap;
@override
Widget build(BuildContext context) {
return Container(
margin: EdgeInsets.only(
bottom: Constant.getActualYPhone(context: context, y: 20)),
child: InkWell(
customBorder: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
),
splashColor: Constant.bgOrange,
// hoverColor: Colors.red,
overlayColor: MaterialStatePropertyAll(Constant.bgOrange),
onTap: () {
onTap(data.typeID);
},
child: Ink(
decoration: BoxDecoration(boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.12),
offset: Offset(0.0, 10), //(x,y)
blurRadius: 4.0,
),
BoxShadow(
color: Colors.grey.withOpacity(0.2),
offset: Offset(0.0, 0), //(x,y)
blurRadius: 2.0,
),
], color: Colors.white, borderRadius: BorderRadius.circular(16)),
child: Column(
children: [
Container(
padding: EdgeInsets.symmetric(
horizontal:
Constant.getActualXPhone(context: context, x: 20),
vertical:
Constant.getActualYPhone(context: context, y: 16)),
child: Column(
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
data.name ?? "",
style: Constant.body_16(context: context),
),
Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
color: Constant.bgBlue),
padding: EdgeInsets.symmetric(
horizontal: Constant.getActualXPhone(
context: context, x: 8),
vertical: Constant.getActualYPhone(
context: context, y: 1)),
child: Text(
data.typename ?? "",
style: Constant.body_12(context: context)
.copyWith(color: Constant.primaryBlue),
),
)
],
),
SizedBox(
height:
Constant.getActualYPhone(context: context, y: 8),
),
Row(
children: [
Row(
children: [
Icon(
EvaIcons.calendar,
color: Constant.primaryOrange,
size: 14,
),
SizedBox(
width: Constant.getActualXPhone(
context: context, x: 8),
),
Text(
data.date ?? "",
style: Constant.titleH2_400_14(context: context)
.copyWith(color: Constant.textDarkGrey),
)
],
),
SizedBox(
width: Constant.getActualXPhone(
context: context, x: 20),
),
Row(
children: [
Icon(
EvaIcons.clock,
color: Constant.primaryOrange,
size: 14,
),
SizedBox(
width: Constant.getActualXPhone(
context: context, x: 8),
),
Text(
data.time ?? "",
style: Constant.titleH2_400_14(context: context)
.copyWith(color: Constant.textDarkGrey),
)
],
),
SizedBox(
width: Constant.getActualXPhone(
context: context, x: 20),
),
Row(
children: [
Image.asset(
'images/finger_tap_orange_botnav.png',
width: Constant.getActualXPhone(
context: context, x: 14),
),
SizedBox(
width: Constant.getActualXPhone(
context: context, x: 8),
),
Text(
data.presensiTypeName ?? "",
style: Constant.titleH2_400_14(context: context)
.copyWith(color: Constant.textDarkGrey),
)
],
),
],
),
SizedBox(
height:
Constant.getActualYPhone(context: context, y: 8),
),
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Icon(
EvaIcons.pin,
color: Constant.primaryOrange,
size: 14,
),
SizedBox(
width: Constant.getActualXPhone(
context: context, x: 8),
),
Flexible(
child: Text(
data.address ?? "",
style: Constant.titleH2_400_14(context: context)
.copyWith(color: Constant.textDarkGrey),
),
)
],
),
if (data.statusID == "1" || data.statusID == "2")
SizedBox(
height:
Constant.getActualYPhone(context: context, y: 20),
),
if (data.statusID == "1" || data.statusID == "2")
Row(
children: List.generate(
1000 ~/ 10,
(index) => Expanded(
child: Container(
color: index % 2 == 0
? Colors.transparent
: Constant.textLightGrey
.withOpacity(0.24),
height: 1,
),
)),
),
],
),
),
AprrovalCardStatusWidget(
statusID: data.statusID ?? "",
statusName: data.statusName ?? "",
)
],
)),
),
);
}
}

View File

@@ -0,0 +1,52 @@
import 'package:absensi_sas/app/constant.dart';
import 'package:flutter/material.dart';
class AprrovalCardStatusWidget extends StatelessWidget {
const AprrovalCardStatusWidget(
{super.key, required this.statusID, required this.statusName});
final String statusID;
final String statusName;
@override
Widget build(BuildContext context) {
switch (statusID) {
case "1":
return Container(
decoration: BoxDecoration(
color: Constant.bgGreen,
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(16),
bottomRight: Radius.circular(16))),
padding: EdgeInsets.symmetric(
horizontal: Constant.getActualXPhone(context: context, x: 20),
vertical: Constant.getActualYPhone(context: context, y: 16)),
width: MediaQuery.of(context).size.width,
child: Text(
statusName,
style: Constant.body_12(context: context)
.copyWith(color: Constant.primaryGreen),
),
);
case "2":
return Container(
decoration: BoxDecoration(
color: Constant.bgRed,
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(16),
bottomRight: Radius.circular(16))),
padding: EdgeInsets.symmetric(
horizontal: Constant.getActualXPhone(context: context, x: 20),
vertical: Constant.getActualYPhone(context: context, y: 16)),
width: MediaQuery.of(context).size.width,
child: Text(
statusName,
style: Constant.body_12(context: context)
.copyWith(color: Constant.primaryRed),
),
);
default:
return Container();
}
}
}

View File

@@ -0,0 +1,75 @@
import 'package:absensi_sas/app/constant.dart';
import 'package:absensi_sas/widget/custom_button_approval.dart';
import 'package:flutter/material.dart';
Future ApprovalDialogConfirmation(
{required BuildContext context,
required String typename,
required String textInformation,
required String staffName,
required Function() confirmFunc}) {
return showDialog(
context: context,
builder: (BuildContext context) => Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.centerRight,
end: Alignment.centerLeft,
colors: [
Color(0xff161C24).withOpacity(0.91),
Color(0xff161C24).withOpacity(0.3)
])),
child: AlertDialog(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
backgroundColor: Colors.white,
surfaceTintColor: Colors.white,
shadowColor: Colors.white,
title: Text(
'Konfirmasi',
style: Constant.titleH1_700_18(context: context),
),
content: Text.rich(TextSpan(
text: "Apakah anda yakin untuk ",
style: Constant.body_16(context: context)
.copyWith(fontWeight: FontWeight.w400),
children: [
TextSpan(
text: textInformation,
style: Constant.body_16(context: context)),
TextSpan(
text: "${typename} ",
style: Constant.body_16(context: context)
.copyWith(fontWeight: FontWeight.w400)),
TextSpan(
text: "${staffName} ?",
style: Constant.body_16(context: context))
])),
actions: <Widget>[
Column(
children: [
Container(
width: MediaQuery.of(context).size.width,
child: CustomButtonOrange(
btnText: "Yakin",
onPressed: confirmFunc,
),
),
SizedBox(
height: 3,
),
Container(
width: MediaQuery.of(context).size.width,
child: CustomButtonWhite(
btnText: "Batal",
onPressed: () {
Navigator.pop(context);
},
),
),
],
),
],
),
),
);
}

View File

@@ -0,0 +1,71 @@
import 'package:absensi_sas/app/constant.dart';
import 'package:absensi_sas/model/approval_type_model.dart';
import 'package:absensi_sas/provider/approval_filter_provider.dart';
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
class ApprovalFilterChipWidget extends HookConsumerWidget {
const ApprovalFilterChipWidget({
super.key,
required this.data,
});
final ApprovalTypeModel data;
@override
Widget build(BuildContext context, WidgetRef ref) {
final currentApprovalFilter = ref.watch(approvalFilterProvider);
handleOnTap(String value) {
var tmp = currentApprovalFilter.selectedApprovalType.toList();
var cek = tmp.contains(value);
if (cek) {
tmp.remove(value);
} else {
tmp.add(value);
}
ref.read(approvalFilterProvider.notifier).state = ApprovalFilterProvider(
endDate: currentApprovalFilter.endDate,
keyword: currentApprovalFilter.keyword,
selectedApprovalType: tmp,
startDate: currentApprovalFilter.startDate);
}
return Container(
margin: EdgeInsets.only(
right: Constant.getActualXPhone(context: context, x: 16)),
child: ElevatedButton(
onPressed: () {
handleOnTap(data.value!);
},
style: ButtonStyle(
elevation: MaterialStatePropertyAll(0),
shape: MaterialStatePropertyAll(RoundedRectangleBorder(
side: BorderSide(
color: currentApprovalFilter.selectedApprovalType
.contains(data.value)
? Constant.textOrange
: Colors.grey.shade300,
),
borderRadius: BorderRadius.circular(16))),
backgroundColor: MaterialStateProperty.resolveWith((states) {
if (currentApprovalFilter.selectedApprovalType
.contains(data.value) ==
true) {
return Constant.textOrange.withOpacity(0.1);
}
return Colors.white;
}),
overlayColor:
MaterialStatePropertyAll(Constant.textOrange.withOpacity(0.48)),
foregroundColor: MaterialStatePropertyAll(Colors.white),
shadowColor: MaterialStatePropertyAll(Colors.white),
surfaceTintColor: MaterialStatePropertyAll(Colors.white)),
child: Text(data.name ?? "",
style: Constant.subtitle_600_14(context: context).copyWith(
color: currentApprovalFilter.selectedApprovalType
.contains(data.value)
? Constant.textOrange
: Colors.black)),
),
);
}
}

View File

@@ -0,0 +1,69 @@
import 'package:absensi_sas/app/constant.dart';
import 'package:absensi_sas/screen/approval/widget/filter_bottom_sheet.dart';
import 'package:fluentui_system_icons/fluentui_system_icons.dart';
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
class ApprovalFilterWidget extends HookConsumerWidget {
const ApprovalFilterWidget({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
return Container(
margin: EdgeInsets.only(
right: Constant.getActualXPhone(context: context, x: 16)),
child: ElevatedButton(
onPressed: () {
showModalBottomSheet(
backgroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(32),
topRight: Radius.circular(32))),
context: context,
isScrollControlled: true,
enableDrag: true,
useSafeArea: true,
// useRootNavigator: true,
builder: (context) {
return FilterBottomSheet();
},
);
},
style: ButtonStyle(
elevation: MaterialStatePropertyAll(0),
shape: MaterialStatePropertyAll(RoundedRectangleBorder(
side: BorderSide(
color: Colors.grey.shade300,
),
borderRadius: BorderRadius.circular(16))),
backgroundColor: MaterialStateProperty.resolveWith((states) {
return Colors.white;
}),
overlayColor:
MaterialStatePropertyAll(Constant.textOrange.withOpacity(0.48)),
foregroundColor: MaterialStatePropertyAll(Colors.white),
shadowColor: MaterialStatePropertyAll(Colors.white),
surfaceTintColor: MaterialStatePropertyAll(Colors.white)),
child: Row(
children: [
Icon(
FluentIcons.options_24_filled,
size: 20,
color: Colors.black,
),
SizedBox(
width: 8,
),
Text(
"Filter",
style: Constant.subtitle_600_14(context: context)
.copyWith(color: Colors.black),
),
],
),
),
);
}
}

View File

@@ -0,0 +1,534 @@
import 'package:absensi_sas/app/constant.dart';
import 'package:absensi_sas/model/approval_type_model.dart';
import 'package:absensi_sas/provider/approval_filter_provider.dart';
import 'package:absensi_sas/widget/custom_button_approval.dart';
import 'package:eva_icons_flutter/eva_icons_flutter.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';
class FilterBottomSheet extends HookConsumerWidget {
const FilterBottomSheet({
super.key,
});
@override
Widget build(BuildContext context, WidgetRef ref) {
final currApprovalTypeState = ref.read(approvalFilterProvider);
final approvalTypeList = [
ApprovalTypeModel(id: "1", name: "Presensi", isActive: false, value: "p"),
ApprovalTypeModel(id: "2", name: "Cuti", isActive: true, value: "c"),
ApprovalTypeModel(id: "3", name: "Lembur", isActive: false, value: "l"),
];
final staffFocuseNode = useFocusNode();
final startDateFocusNode = useFocusNode();
final endDateFocusNode = useFocusNode();
final staffCtr =
useTextEditingController(text: currApprovalTypeState.keyword);
final startDateCtr = useTextEditingController(
text: DateFormat('dd-MM-yyyy').format(currApprovalTypeState.startDate));
final startDateState = useState(currApprovalTypeState.startDate);
final endDateCtr = useTextEditingController(
text: DateFormat('dd-MM-yyyy').format(currApprovalTypeState.endDate));
final endDateState = useState(currApprovalTypeState.endDate);
final dateKey = useState(0);
final selectedApprovalTypeState = useState<List<String>>(
currApprovalTypeState.selectedApprovalType.toList());
final checkboxKey = useState(1000);
selectApprovalTypeFunction(String value) {
var tmp = selectedApprovalTypeState.value;
var cek = tmp.contains(value);
if (cek) {
tmp.remove(value);
} else {
tmp.add(value);
}
selectedApprovalTypeState.value = tmp;
checkboxKey.value = checkboxKey.value + 1;
}
return Container(
padding: EdgeInsets.only(
bottom: MediaQuery.of(context).viewInsets.bottom * 0.75),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(32), topRight: Radius.circular(32))),
child: SingleChildScrollView(
child: Container(
margin: EdgeInsets.symmetric(
horizontal: Constant.getActualXPhone(context: context, x: 24)),
height: MediaQuery.of(context).size.height * 0.75,
child: Column(
children: [
Container(
height: MediaQuery.of(context).size.height * 0.1,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(
"Filter",
style: Constant.titleH1_700(context: context),
),
IconButton(
onPressed: () {
Navigator.pop(context);
},
icon: Icon(Icons.close))
],
),
),
Container(
height: MediaQuery.of(context).size.height * 0.55,
// color: Colors.green,
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Staff",
style: Constant.titleH1_700_18(context: context)
.copyWith(fontWeight: FontWeight.w700),
),
SizedBox(
height:
Constant.getActualYPhone(context: context, y: 16),
),
TextFormField(
controller: staffCtr,
focusNode: staffFocuseNode,
style: Constant.titleH1_500_18(context: context),
decoration: InputDecoration(
focusColor: Constant.textOrange,
// hoverColor: Constant.textOrange,
suffixIcon: Icon(
EvaIcons.searchOutline,
color: staffFocuseNode.hasFocus
? Constant.textOrange
: Color(0xff212B36),
),
label: Text(
"Cari Staff",
style: Constant.titleH1_500_18(context: context)
.copyWith(
color: staffFocuseNode.hasFocus
? Constant.textOrange
: Colors.grey.shade400),
),
hintText: "Cari Staff",
isDense: false,
contentPadding: EdgeInsets.symmetric(
vertical: Constant.getActualYPhone(
context: context, y: 16),
horizontal: Constant.getActualXPhone(
context: context, x: 14)),
hintStyle:
Constant.titleH1_500_18(context: context),
focusedBorder: OutlineInputBorder(
borderSide:
BorderSide(color: Constant.textOrange),
borderRadius: BorderRadius.circular(8)),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Colors.grey.shade500, width: 1),
borderRadius: BorderRadius.circular(8)),
border: OutlineInputBorder(
borderSide:
BorderSide(color: Colors.grey.shade500),
borderRadius: BorderRadius.circular(8))),
),
SizedBox(
height:
Constant.getActualYPhone(context: context, y: 20),
),
Row(
children: [
Expanded(
child: Text(
"Tanggal Awal",
style: Constant.titleH1_700_18(context: context)
.copyWith(fontWeight: FontWeight.w700),
),
),
SizedBox(
width: Constant.getActualXPhone(
context: context, x: 20),
),
Expanded(
child: Text(
"Tanggal Akhir",
style: Constant.titleH1_700_18(context: context)
.copyWith(fontWeight: FontWeight.w700),
),
)
],
),
SizedBox(
height:
Constant.getActualYPhone(context: context, y: 20),
),
Row(
children: [
Expanded(
child: TextFormField(
key: ValueKey(dateKey.value),
controller: startDateCtr,
focusNode: startDateFocusNode,
readOnly: true,
style: Constant.titleH1_500_18(context: context),
decoration: InputDecoration(
focusColor: Constant.textOrange,
// hoverColor: Constant.textOrange,
suffixIcon: Icon(
EvaIcons.calendar,
color: startDateFocusNode.hasFocus
? Constant.textOrange
: Color(0xff212B36),
),
label: Text(
"Tanggal Awal",
style: Constant.titleH1_500_18(
context: context)
.copyWith(
overflow: TextOverflow.ellipsis,
color: startDateFocusNode.hasFocus
? Constant.textOrange
: Colors.grey.shade400),
),
hintText: "Tanggal Awal",
isDense: false,
contentPadding: EdgeInsets.symmetric(
vertical: Constant.getActualYPhone(
context: context, y: 16),
horizontal: Constant.getActualXPhone(
context: context, x: 14)),
hintStyle:
Constant.titleH1_500_18(context: context)
.copyWith(
overflow: TextOverflow.ellipsis),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Constant.textOrange),
borderRadius: BorderRadius.circular(8)),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Colors.grey.shade500,
width: 1),
borderRadius: BorderRadius.circular(8)),
border: OutlineInputBorder(
borderSide: BorderSide(
color: Colors.grey.shade500),
borderRadius: BorderRadius.circular(8))),
onTap: () async {
startDateFocusNode.requestFocus();
dateKey.value = dateKey.value + 1;
DateTime? pickedDate = await showDatePicker(
helpText: "Tanggal Awal",
// locale: Locale('id', 'ID'),
builder: (context, child) {
return Theme(
data: ThemeData().copyWith(
datePickerTheme:
DatePickerThemeData(
dayBackgroundColor:
MaterialStateProperty
.resolveWith(
(states) {
if (states.contains(
MaterialState
.selected)) {
return Constant
.textOrange;
}
return Colors.white;
}),
todayBackgroundColor:
MaterialStateProperty
.resolveWith(
(states) {
if (states.contains(
MaterialState
.selected)) {
return Constant
.textOrange;
}
return Colors.white;
}),
todayBorder: BorderSide(
style:
BorderStyle.solid,
color: Constant
.textOrange,
width: 1),
headerBackgroundColor:
Constant.textOrange,
headerForegroundColor:
Colors.white,
surfaceTintColor:
Colors.white,
backgroundColor:
Colors.white),
primaryColor: Constant.textOrange,
primaryColorDark:
Constant.textOrange,
primaryColorLight:
Constant.textOrange),
child: child!);
},
confirmText: "OK",
cancelText: "Batal",
context: context,
initialDate: startDateState.value,
firstDate: DateTime(1950),
initialEntryMode:
DatePickerEntryMode.calendarOnly,
lastDate: DateTime(2100));
if (pickedDate != null) {
print(
pickedDate); //pickedDate output format => 2021-03-10 00:00:00.000
String formattedDate =
DateFormat('dd-MM-yyyy')
.format(pickedDate);
print(
formattedDate); //formatted date output using intl package => 2021-03-16
startDateCtr.text =
formattedDate; //set output date to TextField value.
startDateState.value = pickedDate;
startDateFocusNode.requestFocus();
} else {}
},
),
),
SizedBox(
width: Constant.getActualXPhone(
context: context, x: 20),
),
Expanded(
child: TextFormField(
key: ValueKey(dateKey.value),
controller: endDateCtr,
focusNode: endDateFocusNode,
readOnly: true,
style: Constant.titleH1_500_18(context: context),
decoration: InputDecoration(
focusColor: Constant.textOrange,
// hoverColor: Constant.textOrange,
suffixIcon: Icon(
EvaIcons.calendar,
color: endDateFocusNode.hasFocus
? Constant.textOrange
: Color(0xff212B36),
),
label: Text(
"Tanggal Akhir",
style: Constant.titleH1_500_18(
context: context)
.copyWith(
overflow: TextOverflow.ellipsis,
color: endDateFocusNode.hasFocus
? Constant.textOrange
: Colors.grey.shade400),
),
hintText: "Tanggal Akhir",
isDense: false,
contentPadding: EdgeInsets.symmetric(
vertical: Constant.getActualYPhone(
context: context, y: 16),
horizontal: Constant.getActualXPhone(
context: context, x: 14)),
hintStyle:
Constant.titleH1_500_18(context: context)
.copyWith(
overflow: TextOverflow.ellipsis),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Constant.textOrange),
borderRadius: BorderRadius.circular(8)),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Colors.grey.shade500,
width: 1),
borderRadius: BorderRadius.circular(8)),
border: OutlineInputBorder(
borderSide: BorderSide(
color: Colors.grey.shade500),
borderRadius: BorderRadius.circular(8))),
onTap: () async {
endDateFocusNode.requestFocus();
dateKey.value = dateKey.value + 1;
DateTime? pickedDate = await showDatePicker(
helpText: "Tanggal Akhir",
// locale: Locale('id', 'ID'),
builder: (context, child) {
return Theme(
data: ThemeData().copyWith(
datePickerTheme:
DatePickerThemeData(
dayBackgroundColor:
MaterialStateProperty
.resolveWith(
(states) {
if (states.contains(
MaterialState
.selected)) {
return Constant
.textOrange;
}
return Colors.white;
}),
todayBackgroundColor:
MaterialStateProperty
.resolveWith(
(states) {
if (states.contains(
MaterialState
.selected)) {
return Constant
.textOrange;
}
return Colors.white;
}),
todayBorder: BorderSide(
style:
BorderStyle.solid,
color: Constant
.textOrange,
width: 1),
headerBackgroundColor:
Constant.textOrange,
headerForegroundColor:
Colors.white,
surfaceTintColor:
Colors.white,
backgroundColor:
Colors.white),
primaryColor: Constant.textOrange,
primaryColorDark:
Constant.textOrange,
primaryColorLight:
Constant.textOrange),
child: child!);
},
confirmText: "OK",
cancelText: "Batal",
context: context,
initialDate: startDateState.value,
firstDate: DateTime(1950),
initialEntryMode:
DatePickerEntryMode.calendarOnly,
lastDate: DateTime(2100));
if (pickedDate != null) {
print(
pickedDate); //pickedDate output format => 2021-03-10 00:00:00.000
String formattedDate =
DateFormat('dd-MM-yyyy')
.format(pickedDate);
print(
formattedDate); //formatted date output using intl package => 2021-03-16
endDateCtr.text =
formattedDate; //set output date to TextField value.
endDateState.value = pickedDate;
endDateFocusNode.requestFocus();
} else {}
},
),
)
],
),
SizedBox(
height:
Constant.getActualYPhone(context: context, y: 16),
),
// Text(
// ref
// .watch(approvalFilterProvider)
// .selectedApprovalType
// .toString(),
// style: Constant.titleH1_700_18(context: context),
// ),
Text(
"Approval",
style: Constant.titleH1_700_18(context: context)
.copyWith(fontWeight: FontWeight.w700),
),
SizedBox(
height:
Constant.getActualYPhone(context: context, y: 16),
),
...approvalTypeList.map(
(e) {
return Row(
children: [
Checkbox(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(4)),
value: selectedApprovalTypeState.value
.contains(e.value),
onChanged: (val) {
selectApprovalTypeFunction(e.value!);
},
),
Text(
e.name ?? "",
style: Constant.titleH1_700_18(context: context)
.copyWith(fontWeight: FontWeight.normal),
)
],
);
},
)
],
),
),
),
Container(
height: MediaQuery.of(context).size.height * 0.1,
child: Row(
children: [
Expanded(
child: CustomButtonWhite(
btnText: "Reset",
onPressed: () {
ref.read(approvalFilterProvider.notifier).state =
ApprovalFilterProvider(
startDate: DateTime.now(),
endDate: DateTime.now(),
keyword: "",
selectedApprovalType: []);
Navigator.pop(context);
}),
),
SizedBox(
width: Constant.getActualXPhone(context: context, x: 24),
),
Expanded(
child: CustomButtonOrange(
btnText: "Simpan",
onPressed: () {
ref.read(approvalFilterProvider.notifier).state =
ApprovalFilterProvider(
endDate: endDateState.value,
keyword: staffCtr.text,
selectedApprovalType:
selectedApprovalTypeState.value,
startDate: startDateState.value);
Navigator.pop(context);
}),
),
],
),
)
],
),
),
),
);
}
}

View File

@@ -0,0 +1,368 @@
import 'dart:async';
import 'dart:math';
import 'package:absensi_sas/app/constant.dart';
import 'package:absensi_sas/model/approval_detail_model.dart';
import 'package:absensi_sas/screen/approval/approval_mockup.dart';
import 'package:absensi_sas/screen/approval/widget/approval_dialog_confirmation.dart';
import 'package:absensi_sas/widget/custom_button_approval.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:flutter_map/flutter_map.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:latlong2/latlong.dart';
class ApprovalDetailScreen extends HookConsumerWidget {
const ApprovalDetailScreen({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final loading = useState(true);
final data = useState<ApprovalDetailModel>(
ApprovalDetailModel(imagePath: "", lat: 0, long: 0));
init() {
loading.value = true;
var randIdx = Random().nextInt(approvalDetailMockup.length);
var tmp = approvalDetailMockup[randIdx];
Timer(Duration(seconds: 2), () {
data.value = tmp;
loading.value = false;
});
}
useEffect(() {
WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
init();
return;
});
return () {};
}, []);
return SafeArea(
minimum: EdgeInsets.only(
top: Constant.getActualYPhone(context: context, y: 30)),
child: Scaffold(
backgroundColor: Constant.textWhite,
appBar: AppBar(
backgroundColor: Colors.white,
shadowColor: Colors.white,
surfaceTintColor: Colors.white,
scrolledUnderElevation: 0,
elevation: 0,
leading: Container(
child: IconButton(
onPressed: () {
Navigator.pop(context);
},
icon: Icon(Icons.arrow_back_ios_new_rounded)),
),
centerTitle: true,
title: Text(
"Detail",
style: Constant.title_screen(context: context),
),
),
body: loading.value
? Center(
child: CircularProgressIndicator(),
)
: Container(
color: Colors.white,
padding: EdgeInsets.symmetric(
horizontal:
Constant.getActualXPhone(context: context, x: 20)),
child: RefreshIndicator(
onRefresh: () async {
init();
},
child: SingleChildScrollView(
child: Column(
children: [
SizedBox(
height: Constant.getActualYPhone(
context: context, y: 36),
),
if (data.value.statusID == "1" ||
data.value.statusID == "2")
Row(
children: [
Spacer(),
if (data.value.statusID == "1")
Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
color: Constant.bgGreen),
padding: EdgeInsets.symmetric(
horizontal: Constant.getActualXPhone(
context: context, x: 8),
vertical: Constant.getActualYPhone(
context: context, y: 2)),
child: Text(
"Approve",
style: Constant.body_12(context: context)
.copyWith(
color: Constant.primaryGreen),
),
),
if (data.value.statusID == "2")
Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
color: Constant.bgRed),
padding: EdgeInsets.symmetric(
horizontal: Constant.getActualXPhone(
context: context, x: 8),
vertical: Constant.getActualYPhone(
context: context, y: 2)),
child: Text(
"Reject",
style: Constant.body_12(context: context)
.copyWith(color: Constant.primaryRed),
),
)
],
),
if (data.value.statusID == "1" ||
data.value.statusID == "2")
SizedBox(
height: Constant.getActualYPhone(
context: context, y: 8),
),
Container(
margin: EdgeInsets.only(bottom: 8),
child: Row(
children: [
Expanded(
flex: 4,
child: Text("Nama : ",
style: Constant.body_16(context: context)
.copyWith(
color: Constant.textDarkGrey,
fontWeight: FontWeight.w400)),
),
Expanded(
flex: 9,
child: Text(data.value.name ?? "",
style: Constant.body_16(context: context)
.copyWith(
fontWeight: FontWeight.w400)),
)
],
),
),
Container(
margin: EdgeInsets.only(bottom: 8),
child: Row(
children: [
Expanded(
flex: 4,
child: Text("NIP : ",
style: Constant.body_16(context: context)
.copyWith(
color: Constant.textDarkGrey,
fontWeight: FontWeight.w400)),
),
Expanded(
flex: 9,
child: Text(data.value.nip ?? "",
style: Constant.body_16(context: context)
.copyWith(
fontWeight: FontWeight.w400)),
)
],
),
),
Container(
margin: EdgeInsets.only(bottom: 8),
child: Row(
children: [
Expanded(
flex: 4,
child: Text("Tanggal : ",
style: Constant.body_16(context: context)
.copyWith(
color: Constant.textDarkGrey,
fontWeight: FontWeight.w400)),
),
Expanded(
flex: 9,
child: Text(data.value.date ?? "",
style: Constant.body_16(context: context)
.copyWith(
fontWeight: FontWeight.w400)),
)
],
),
),
Container(
margin: EdgeInsets.only(bottom: 8),
child: Row(
children: [
Expanded(
flex: 4,
child: Text("Jam : ",
style: Constant.body_16(context: context)
.copyWith(
color: Constant.textDarkGrey,
fontWeight: FontWeight.w400)),
),
Expanded(
flex: 9,
child: Text(data.value.time ?? "",
style: Constant.body_16(context: context)
.copyWith(
fontWeight: FontWeight.w400)),
)
],
),
),
Container(
margin: EdgeInsets.only(bottom: 8),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
flex: 4,
child: Text("Alamat : ",
style: Constant.body_16(context: context)
.copyWith(
color: Constant.textDarkGrey,
fontWeight: FontWeight.w400)),
),
Expanded(
flex: 9,
child: Text(data.value.address ?? "",
style: Constant.body_16(context: context)
.copyWith(
fontWeight: FontWeight.w400)),
)
],
),
),
Container(
margin: EdgeInsets.only(top: 16),
width: MediaQuery.of(context).size.width,
height: Constant.getActualYPhone(
context: context, y: 400),
child: FlutterMap(
options: MapOptions(
center: LatLng(data.value.lat ?? 0,
data.value.long ?? 0),
zoom: 16,
),
children: [
TileLayer(
urlTemplate:
'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
userAgentPackageName: 'com.example.app',
),
MarkerLayer(
markers: [
Marker(
point: LatLng(-7.539538, 110.798357),
child: const Icon(
Icons.location_on,
color: Colors.red,
size: 50,
),
)
// Marker(
// point: LatLng(lat, long),
// width: 100,
// height: 100,
// builder: (context) => const Icon(
// Icons.location_on,
// color: Colors.red,
// size: 50,
// )),
],
),
]),
),
if (data.value.imagePath != "" &&
data.value.imagePath != null)
Container(
margin: EdgeInsets.only(top: 16, bottom: 48),
child: Image.network(
data.value.imagePath!,
errorBuilder: (context, error, stackTrace) {
return Text(
"Error load image : ${error.toString()}",
style: Constant.body_12(context: context)
.copyWith(color: Constant.primaryRed),
);
},
loadingBuilder:
(context, child, loadingProgress) {
if (loadingProgress == null) return child;
return Container(
child: Column(
children: [
Text(loadingProgress.expectedTotalBytes !=
null
? ((loadingProgress.cumulativeBytesLoaded /
loadingProgress
.expectedTotalBytes!) *
100)
.round()
.toString() +
" %"
: ""),
LinearProgressIndicator(
value: loadingProgress
.expectedTotalBytes !=
null
? loadingProgress
.cumulativeBytesLoaded /
loadingProgress
.expectedTotalBytes!
: null,
),
],
));
},
),
),
if (data.value.statusID != "1" &&
data.value.statusID != "2")
Container(
margin: EdgeInsets.symmetric(vertical: 24),
child: Row(
children: [
Expanded(
child: CustomButtonWhite(
btnText: "Reject",
onPressed: () {
ApprovalDialogConfirmation(
context: context,
typename: "presensi ",
textInformation: "menolak ",
staffName: data.value.name ?? "",
confirmFunc: () {});
}),
),
SizedBox(
width: Constant.getActualXPhone(
context: context, x: 24),
),
Expanded(
child: CustomButtonOrange(
btnText: "Approve",
onPressed: () {
ApprovalDialogConfirmation(
context: context,
typename: "presensi ",
textInformation: "menyetujui ",
staffName: data.value.name ?? "",
confirmFunc: () {});
}),
),
],
),
)
],
),
),
),
),
));
}
}

View File

@@ -0,0 +1,77 @@
import 'package:absensi_sas/app/constant.dart';
import 'package:flutter/material.dart';
class CustomButtonOrange extends StatelessWidget {
const CustomButtonOrange({super.key, required this.btnText, this.onPressed});
final Function()? onPressed;
final String btnText;
@override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(boxShadow: [
BoxShadow(
color: Constant.textOrange.withOpacity(0.24),
spreadRadius: 0,
blurRadius: 16,
offset: Offset(0, 8), // changes position of shadow
),
]),
child: ElevatedButton(
style: ButtonStyle(
elevation: MaterialStatePropertyAll(0),
shape: MaterialStatePropertyAll(RoundedRectangleBorder(
side: BorderSide(
color: Constant.textOrange,
),
borderRadius: BorderRadius.circular(8))),
backgroundColor: MaterialStateProperty.resolveWith((states) {
return Constant.textOrange;
}),
overlayColor:
MaterialStatePropertyAll(Colors.white.withOpacity(0.3)),
foregroundColor: MaterialStatePropertyAll(Constant.textOrange),
shadowColor: MaterialStatePropertyAll(Constant.textOrange),
surfaceTintColor: MaterialStatePropertyAll(Constant.textOrange)),
onPressed: onPressed,
child: Text(
btnText,
style: Constant.body_14(context: context)
.copyWith(color: Colors.white, fontWeight: FontWeight.w700),
)),
);
}
}
class CustomButtonWhite extends StatelessWidget {
const CustomButtonWhite({super.key, required this.btnText, this.onPressed});
final Function()? onPressed;
final String btnText;
@override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: onPressed,
child: Text(
btnText,
style: Constant.body_14(context: context)
.copyWith(color: Colors.black, fontWeight: FontWeight.w700),
),
style: ButtonStyle(
elevation: MaterialStatePropertyAll(0),
shape: MaterialStatePropertyAll(RoundedRectangleBorder(
side: BorderSide(
color: Colors.grey.shade300,
),
borderRadius: BorderRadius.circular(8))),
backgroundColor: MaterialStateProperty.resolveWith((states) {
return Colors.white;
}),
overlayColor:
MaterialStatePropertyAll(Constant.textOrange.withOpacity(0.48)),
foregroundColor: MaterialStatePropertyAll(Colors.white),
shadowColor: MaterialStatePropertyAll(Colors.white),
surfaceTintColor: MaterialStatePropertyAll(Colors.white)),
);
}
}