Fakultas Ilmu Komputer UI

Commit 8ec962cd authored by Tsamara Esperanti Erwin's avatar Tsamara Esperanti Erwin 🦄
Browse files

Merge branch 'PBI-7-Progress_Program' into 'staging'

Pbi 7 progress program

add progress program, increase coverage

See merge request !88
parents 0d106b71 013ec5f0
Pipeline #61046 passed with stages
in 31 minutes and 22 seconds
......@@ -6,7 +6,7 @@ String formatDate(DateTime dateTime) {
}
String formatDatePlusOne(DateTime dateTime) {
var addOneDay = dateTime.add(Duration(days: 1));
final addOneDay = dateTime.add(const Duration(days: 1));
final formatter = DateFormat(DateFormat.YEAR_MONTH_DAY);
return dateTime != null ? formatter.format(addOneDay.toLocal()) : '';
}
......
......@@ -31,6 +31,8 @@ import 'package:home_industry/Pages/Transactions/repositories/transaction_reposi
import 'package:home_industry/State/Auth/repositories/depedencies_repositories.dart';
import 'package:home_industry/State/ChangeBottomNavigatonBar/bloc.dart';
import '../Pages/Program/repositories/program_repository.dart';
class Router {
static const registerPage = '/register';
static const editProfilePage = '/edit-profile';
......@@ -147,14 +149,29 @@ class Router {
case programs:
return MaterialPageRoute<Programs>(
builder: (context) => const Programs());
builder: (context) => RepositoryProvider<ProgramRepository>.value(
value: ProgramRepository(
dio:
RepositoryProvider.of<DependenciesRepositories>(
context)
.dio),
child: Programs()
));
case detailProgramPage:
final Map<String, dynamic> args = settings.arguments;
return MaterialPageRoute<DetailProgram>(
builder: (context) => DetailProgram(
program: args['program'],
));
builder: (context) => RepositoryProvider<ProgramRepository>.value(
value: ProgramRepository(
dio:
RepositoryProvider.of<DependenciesRepositories>(
context)
.dio),
child: DetailProgram(
program: args['program'],
),
));
case donationPage:
final Map<String, dynamic> args = settings.arguments;
......
......@@ -43,7 +43,6 @@ class ChooseDonationDialog extends StatelessWidget {
class _ChooseDonation extends StatefulWidget {
final Program program;
// const _ChooseDonation(this.program);
const _ChooseDonation({Key key, @required this.program}) : super(key: key);
@override
......@@ -59,6 +58,7 @@ class __ChooseDonationState extends State<_ChooseDonation> {
return Column(
children: [
CheckboxListTile(
key: const Key('uang'),
dense: true,
controlAffinity: ListTileControlAffinity.leading,
value: uangValue,
......@@ -74,6 +74,7 @@ class __ChooseDonationState extends State<_ChooseDonation> {
});
}),
CheckboxListTile(
key: const Key('barang'),
dense: true,
controlAffinity: ListTileControlAffinity.leading,
title: Text('Barang',
......
......@@ -39,10 +39,10 @@ class _DonasiBarangState extends State<DonasiBarang> {
void onTap(
{@required String quantity,
@required String description,
@required String methodOfDelivery,
@required bool isFinal,
String address}) {
@required String description,
@required String methodOfDelivery,
@required bool isFinal,
String address}) {
goodsDonationBloc.add(GoodsDonationButtonClicked(
program: widget.program.id,
quantity: quantity,
......@@ -110,7 +110,7 @@ class _DonasiBarangState extends State<DonasiBarang> {
),
),
Container(
margin: EdgeInsets.symmetric(horizontal: 20.0),
margin: const EdgeInsets.symmetric(horizontal: 20.0),
child: Form(
key: _formKey,
child: Column(
......@@ -120,7 +120,7 @@ class _DonasiBarangState extends State<DonasiBarang> {
Row(
children: [
Radio(
key: Key('PCK-button'),
key: const Key('PCK-button'),
value: 'PCK',
groupValue: methodOfDelivery,
onChanged: (val) {
......@@ -128,7 +128,7 @@ class _DonasiBarangState extends State<DonasiBarang> {
methodOfDelivery = val;
deliveryWidget = Row(
crossAxisAlignment:
CrossAxisAlignment.start,
CrossAxisAlignment.start,
children: [
RichText(
text: TextSpan(
......@@ -144,8 +144,8 @@ class _DonasiBarangState extends State<DonasiBarang> {
.textTheme
.bodyText2
.copyWith(
fontSize: 18,
color: Colors.red)),
fontSize: 18,
color: Colors.red)),
],
),
),
......@@ -157,7 +157,7 @@ class _DonasiBarangState extends State<DonasiBarang> {
child: TextFormField(
controller: addressController,
keyboardType:
TextInputType.multiline,
TextInputType.multiline,
validator: (value) {
if (value.isEmpty) {
return 'Mohon isi alamat';
......@@ -179,7 +179,7 @@ class _DonasiBarangState extends State<DonasiBarang> {
.copyWith(fontSize: 18),
),
Radio(
key: Key('DLV-button'),
key: const Key('DLV-button'),
value: 'DLV',
groupValue: methodOfDelivery,
onChanged: (val) {
......@@ -217,7 +217,7 @@ class _DonasiBarangState extends State<DonasiBarang> {
setState(() {
deliveryWidget = Container(
height: 80,
child: Text(
child: const Text(
'Mohon pilih metode pengiriman',
style: TextStyle(color: Colors.red),
),
......@@ -252,20 +252,20 @@ class _DonasiBarangState extends State<DonasiBarang> {
borderRadius: BorderRadius.circular(50)),
child: Center(
child: Text(
'DONASI SEKARANG',
style: Theme.of(context).textTheme.bodyText2.copyWith(
fontSize: 18,
color: Colors.white,
fontWeight: FontWeight.bold,
letterSpacing: 1.2),
)),
'DONASI SEKARANG',
style: Theme.of(context).textTheme.bodyText2.copyWith(
fontSize: 18,
color: Colors.white,
fontWeight: FontWeight.bold,
letterSpacing: 1.2),
)),
),
),
);
}
List<Widget> _getGoodsForm(){
var goodsTextFields = <Widget>[];
final goodsTextFields = <Widget>[];
for(var i = 0; i < goodsDescList.length; i++){
goodsTextFields.add(
Padding(
......@@ -286,30 +286,30 @@ class _DonasiBarangState extends State<DonasiBarang> {
return Padding(
padding: const EdgeInsets.only(top: 15.0),
child: Row(
children: [
Container(
children: [
Container(
width: MediaQuery.of(context).size.width - 80,
child: Divider(color: Colors.grey)),
const SizedBox(width: 10.0,),
InkWell(
key: Key('addRemoveButton' + index.toString()),
onTap: (){
if(add){
goodsDescList.insert(index+1, null);
goodsQtyList.insert(index+1, null);
}
else {
goodsDescList.removeAt(index);
goodsQtyList.removeAt(index);
}
setState((){});
child: Divider(color: Colors.grey)),
const SizedBox(width: 10.0,),
InkWell(
key: Key('addRemoveButton$index'),
onTap: (){
if(add){
goodsDescList.insert(index+1, null);
goodsQtyList.insert(index+1, null);
}
else {
goodsDescList.removeAt(index);
goodsQtyList.removeAt(index);
}
setState((){});
},
child: Icon(
(add) ? Icons.add_circle : Icons.remove_circle,
color: Color.fromRGBO(60, 141, 188, 1),
),
)
],
child: Icon(
add ? Icons.add_circle : Icons.remove_circle,
color: const Color.fromRGBO(60, 141, 188, 1),
),
)
],
),
);
}
......@@ -317,7 +317,7 @@ class _DonasiBarangState extends State<DonasiBarang> {
class GoodsForm extends StatefulWidget {
final int index;
GoodsForm({Key key, @required this.index})
const GoodsForm({Key key, @required this.index})
: super(key: key);
@override
......@@ -362,7 +362,7 @@ class _GoodsFormState extends State<GoodsForm> {
width: 187,
height: 50,
child: TextFormField(
key: Key('goods-desc'),
key: const Key('goods-desc'),
onChanged: (v) => _DonasiBarangState.goodsDescList[widget.index] = v,
controller: _goodsDescriptionController,
validator: (value) {
......@@ -403,7 +403,7 @@ class _GoodsFormState extends State<GoodsForm> {
height: 50,
color: Colors.white,
child: TextFormField(
key: Key('goods-qty'),
key: const Key('goods-qty'),
onChanged: (v) => _DonasiBarangState.goodsQtyList[widget.index] = v,
controller: _goodsQuantityController,
validator: (value) {
......@@ -433,4 +433,4 @@ InputDecoration _inputDecoration() {
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color: Color.fromRGBO(224, 225, 226, 1))),
);
}
}
\ No newline at end of file
......@@ -61,13 +61,13 @@ class OTPPage extends StatelessWidget {
children: <Widget>[
const Text('Mohon masukkan OTP yang dikirim ke nomor anda',
softWrap: true),
Divider(
const Divider(
height: 20,
thickness: null,
),
Theme(
data: Theme.of(context)
.copyWith(inputDecorationTheme: InputDecorationTheme()),
.copyWith(inputDecorationTheme: const InputDecorationTheme()),
child: PinPut(
onSubmit: (String pin) {
_otpSubmitted(pin, context);
......@@ -85,7 +85,7 @@ class OTPPage extends StatelessWidget {
),
),
Container(
margin: EdgeInsets.only(top: 10),
margin: const EdgeInsets.only(top: 10),
height: MediaQuery.of(context).size.height / 18,
child: ButtonResendOTP(
label: 'Kirim Ulang OTP',
......
......@@ -12,7 +12,7 @@ abstract class SearchProductState extends Equatable {
}
class InitialSearchProductState extends SearchProductState {
const InitialSearchProductState() : super(0, '', hasReachedMax: true);
const InitialSearchProductState({hasReachedMax}) : super(0, '', hasReachedMax: true);
@override
List<Object> get props => [page, searchQuery];
......
......@@ -20,6 +20,9 @@ class ProgramBloc extends Bloc<ProgramEvent, ProgramState> {
bool _hasReachedMax(ProgramState state) =>
state is ListProgramsLoaded && state.hasReachedMax;
bool _hasReachedMaxSorted(ProgramState state) =>
state is ListProgramsSortedLoaded && state.hasReachedMax;
@override
Stream<ProgramState> mapEventToState(
ProgramEvent event,
......@@ -48,6 +51,30 @@ class ProgramBloc extends Bloc<ProgramEvent, ProgramState> {
} catch (e) {
yield ListProgramsError(error: e.toString());
}
} else if ((event is FetchProgramByStatus) && !_hasReachedMaxSorted(currentState)) {
try {
var programs =
await programRepository.fetchProgram(page: currentState.page + 1);
if (currentState is ListProgramsLoaded || currentState is InitialListProgramState) {
programs.results.sort((a,b) => b.endDateTime.compareTo(a.endDateTime));
yield ListProgramsSortedLoaded(
programs: programs.results,
hasReachedMax: programs.next == null,
page: currentState.page + 1);
return;
} else if (currentState is ListProgramsSortedLoaded) {
programs.results.sort((a,b) => b.endDateTime.compareTo(a.endDateTime));
yield ListProgramsSortedLoaded(
programs: currentState.programs + programs.results,
hasReachedMax: programs.next == null,
page: currentState.page + 1);
return;
}
} on DioError catch (e) {
yield ListProgramsError(error: e.response.data.toString());
} catch (e) {
yield ListProgramsError(error: e.toString());
}
}
}
......
......@@ -10,3 +10,10 @@ class FetchProgram extends ProgramEvent {
@override
List<Object> get props => [];
}
class FetchProgramByStatus extends ProgramEvent {
const FetchProgramByStatus();
@override
List<Object> get props => [];
}
......@@ -26,6 +26,18 @@ class ListProgramsLoaded extends ProgramState {
List<Object> get props => [programs, hasReachedMax, page];
}
class ListProgramsSortedLoaded extends ProgramState {
final List<Program> programs;
final bool hasReachedMax;
const ListProgramsSortedLoaded(
{@required this.programs, @required this.hasReachedMax, @required page})
: super(page);
@override
List<Object> get props => [programs, hasReachedMax, page];
}
class ListProgramsError extends ProgramState {
final String error;
const ListProgramsError({@required this.error}) : super(0);
......@@ -33,3 +45,13 @@ class ListProgramsError extends ProgramState {
@override
List<Object> get props => [page];
}
class ListProgramsSortedError extends ProgramState {
final String error;
const ListProgramsSortedError({@required this.error}) : super(0);
@override
List<Object> get props => [page];
}
import 'dart:async';
import 'package:bloc/bloc.dart';
import 'package:dio/dio.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/cupertino.dart';
import 'package:home_industry/Pages/Program/model/program.dart';
import 'package:home_industry/Pages/Program/repositories/program_repository.dart';
part 'progress_program_event.dart';
part 'progress_program_state.dart';
class ProgressProgramBloc extends Bloc<ProgressProgramEvent, ProgressProgramState> {
final Program program;
final ProgramRepository progressProgramRepository;
ProgressProgramBloc({@required this.progressProgramRepository, @required this.program})
: assert(progressProgramRepository != null);
@override
ProgressProgramState get initialState => const ProgressProgramInitial();
@override
Stream<ProgressProgramState> mapEventToState(ProgressProgramEvent event) async* {
if (event is FetchProgressProgram) {
try {
final progressProgram = await progressProgramRepository.fetchProgressProgram(program.id);
yield ProgressProgramLoaded(progressProgram);
} on DioError catch (_) {
yield ProgressProgramError(error: _.response.toString());
}
}
}
}
\ No newline at end of file
part of 'progress_program_bloc.dart';
abstract class ProgressProgramEvent extends Equatable {
const ProgressProgramEvent();
}
class FetchProgressProgram extends ProgressProgramEvent {
const FetchProgressProgram();
@override
List<Object> get props => [];
}
part of 'progress_program_bloc.dart';
abstract class ProgressProgramState extends Equatable {
const ProgressProgramState();
}
class ProgressProgramInitial extends ProgressProgramState {
const ProgressProgramInitial();
@override
List<Object> get props => [];
}
class ProgressProgramLoaded extends ProgressProgramState {
final List<dynamic> progressList;
const ProgressProgramLoaded(this.progressList);
@override
List<Object> get props => [];
}
class ProgressProgramError extends ProgressProgramState {
final String error;
const ProgressProgramError({@required this.error}) : super();
@override
List<Object> get props => [];
}
This diff is collapsed.
......@@ -14,9 +14,17 @@ import 'package:home_industry/Pages/Program/program_search_delegate.dart';
import 'package:home_industry/Pages/Program/repositories/program_repository.dart';
import 'package:home_industry/State/Auth/repositories/depedencies_repositories.dart';
class Programs extends StatelessWidget {
class Programs extends StatefulWidget {
const Programs({Key key}) : super(key: key);
@override
_ProgramsState createState() => _ProgramsState();
}
class _ProgramsState extends State<Programs> {
Widget programList = const _ListProgram();
String sort = 'Status';
Future<void> onSearchPressed(BuildContext context) async {
final searchProgram =
SearchProgramBloc(RepositoryProvider.of<ProgramRepository>(context));
......@@ -31,35 +39,66 @@ class Programs extends StatelessWidget {
create: (context) => ProgramRepository(
dio: RepositoryProvider.of<DependenciesRepositories>(context).dio),
child: Builder(
builder: (BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(
'Program',
style: TextStyle(color: Colors.black),
builder: (BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(
'Program',
style: TextStyle(color: Colors.black),
),
centerTitle: true,
backgroundColor: Colors.white,
elevation: 0.5,
actions: <Widget>[
IconButton(
icon: const Icon(Icons.search),
onPressed: () async {
await onSearchPressed(context);
})
],
),
centerTitle: true,
backgroundColor: Colors.white,
elevation: 0.5,
actions: <Widget>[
IconButton(
icon: const Icon(Icons.search),
onPressed: () async {
await onSearchPressed(context);
})
],
),
body: BlocProvider(
create: (context) => ProgramBloc(
programRepository:
body: SingleChildScrollView(
child: BlocProvider(
create: (context) => ProgramBloc(
programRepository:
RepositoryProvider.of<ProgramRepository>(context))
..add(const FetchProgram()),
child: const _ListProgram(),
),
);
},
..add(const FetchProgram()),
child: Column(children: <Widget>[
programList
]),
),
),
bottomNavigationBar: Container(
margin: EdgeInsets.symmetric(horizontal: 25.0),
child: Wrap(children: [
Padding(
padding: const EdgeInsets.only(top: 20),
child: Text('Urutkan Berdasarkan:',
style: TextStyle(fontSize: 16.0),),
),
GestureDetector(
onTap: () {
setState(() {
programList = _ListProgramSortStatus();
});
},
child: FilterButton(sort: 'Status')
),
GestureDetector(
onTap: () {
setState(() {
programList = _ListProgram();
});
},
child: FilterButton(sort: 'Tanggal')
),
]),
),
);
},
),
);
}
}
......@@ -135,12 +174,124 @@ class __ListProgramState extends State<_ListProgram> {
}),
);
}
} else if (state is ListProgramsSortedLoaded) {
if (state.programs.isEmpty) {
return const Center(
child: Text(
'Maaf program sementara kosong',
style: TextStyle(fontSize: 20),
),
);
} else {
return SizedBox(