Commit 4c625184 authored by Ilham Maulana's avatar Ilham Maulana 💻

fix: filter member loan widgets

parent 8424e274
...@@ -14,4 +14,15 @@ class Loan { ...@@ -14,4 +14,15 @@ class Loan {
this.remainingDays, this.remainingDays,
this.isOverdue, this.isOverdue,
); );
factory Loan.fromJson(Map<String, dynamic> data) {
final book = Book.fromJson(data["book_detail"]);
return Loan(
book,
data["loan_date"],
data["due_date"],
data["remaining_loan_time"],
data["is_overdue"],
);
}
} }
...@@ -5,6 +5,7 @@ import 'package:http/http.dart' as http; ...@@ -5,6 +5,7 @@ import 'package:http/http.dart' as http;
import 'package:library_app/src/models/token.dart'; import 'package:library_app/src/models/token.dart';
import 'package:library_app/src/models/user.dart'; import 'package:library_app/src/models/user.dart';
// Flutter: make memberLoans adjustable to be filtered to near outstanding loan and overdued loan
class AuthProvider with ChangeNotifier { class AuthProvider with ChangeNotifier {
String baseUrl = 'http://localhost:8000/api/v1'; String baseUrl = 'http://localhost:8000/api/v1';
...@@ -14,6 +15,9 @@ class AuthProvider with ChangeNotifier { ...@@ -14,6 +15,9 @@ class AuthProvider with ChangeNotifier {
bool invalidUsernameOrPassword = false; bool invalidUsernameOrPassword = false;
List<dynamic>? memberLoans; List<dynamic>? memberLoans;
bool filterByUpcoming = false;
bool filterByOverdued = false;
Future<void> signIn(String username, String password) async { Future<void> signIn(String username, String password) async {
try { try {
final response = await http.post( final response = await http.post(
...@@ -157,10 +161,19 @@ class AuthProvider with ChangeNotifier { ...@@ -157,10 +161,19 @@ class AuthProvider with ChangeNotifier {
} }
} }
Future<void> getMemberLoan(int id) async { Future<void> getMemberLoan() async {
String url = '$baseUrl/members/${user?.accountId}/loans/';
if (filterByUpcoming) {
url += '?near_outstanding=True';
} else if (filterByOverdued) {
url += '?overdue=True';
} else {
null;
}
try { try {
final response = await http.get( final response = await http.get(
Uri.parse('$baseUrl/members/$id/loans/'), Uri.parse(url),
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
'Authorization': 'Bearer ${token?.key}' 'Authorization': 'Bearer ${token?.key}'
...@@ -181,6 +194,16 @@ class AuthProvider with ChangeNotifier { ...@@ -181,6 +194,16 @@ class AuthProvider with ChangeNotifier {
} }
} }
void setFilterUpcoming() {
filterByUpcoming = !filterByUpcoming;
notifyListeners();
}
void setFilterOverdued() {
filterByOverdued = !filterByOverdued;
notifyListeners();
}
Future<void> createMemberLoan(int memberId, int bookId, int loanDay) async { Future<void> createMemberLoan(int memberId, int bookId, int loanDay) async {
final now = DateTime.now(); final now = DateTime.now();
final dueDate = now.add(Duration(days: loanDay)); final dueDate = now.add(Duration(days: loanDay));
......
...@@ -18,18 +18,16 @@ class _LoanList extends State<LoanList> { ...@@ -18,18 +18,16 @@ class _LoanList extends State<LoanList> {
@override @override
void initState() { void initState() {
super.initState(); super.initState();
Provider.of<AuthProvider>(context, listen: false) Provider.of<AuthProvider>(context, listen: false).getMemberLoan();
.getMemberLoan(widget.memberId);
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Consumer<AuthProvider>(builder: (context, authProvider, child) { return Consumer<AuthProvider>(builder: (context, authProvider, child) {
if (authProvider.memberLoans != null) { if (authProvider.memberLoans != null) {
final Iterable<Loan> loans = authProvider.memberLoans!.map( var loans = authProvider.memberLoans!.map(
(loan) { (loan) {
Map<String, dynamic> bookMap = loan["book_detail"]; var book = Book.fromJson(loan["book_detail"]);
final book = Book.fromJson(bookMap);
return Loan( return Loan(
book, book,
loan["loan_date"], loan["loan_date"],
...@@ -39,6 +37,7 @@ class _LoanList extends State<LoanList> { ...@@ -39,6 +37,7 @@ class _LoanList extends State<LoanList> {
); );
}, },
); );
return NestedScrollView( return NestedScrollView(
headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) { headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
return [const TopAppBar(title: "Book Loans")]; return [const TopAppBar(title: "Book Loans")];
...@@ -78,8 +77,7 @@ class _TopAppBar extends State<TopAppBar> { ...@@ -78,8 +77,7 @@ class _TopAppBar extends State<TopAppBar> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final screenSize = MediaQuery.of(context).size; return Consumer<AuthProvider>(builder: (context, authBuilder, child) {
return SliverAppBar( return SliverAppBar(
title: Text(showWidget ? "" : title), title: Text(showWidget ? "" : title),
actions: [ actions: [
...@@ -92,38 +90,27 @@ class _TopAppBar extends State<TopAppBar> { ...@@ -92,38 +90,27 @@ class _TopAppBar extends State<TopAppBar> {
showWidget = !showWidget; showWidget = !showWidget;
}); });
}, },
icon: Icon(showWidget ? Icons.close : Icons.filter_alt_outlined), icon: Icon(
showWidget ? Icons.close : Icons.filter_alt_outlined,
),
), ),
Offstage( Offstage(
offstage: !showWidget, offstage: !showWidget,
child: Row( child: const Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
FilledButton( Row(
style: TextButton.styleFrom( children: [Text("Upcoming"), SwitchToUpcoming()],
padding: EdgeInsets.symmetric(
horizontal: screenSize.width * 0.04,
vertical: screenSize.width * 0.02),
),
onPressed: () {},
child: const Text('Near Outstanding'),
),
const SizedBox(
width: 8.0,
),
FilledButton(
style: TextButton.styleFrom(
padding: EdgeInsets.symmetric(
horizontal: screenSize.width * 0.04,
vertical: screenSize.width * 0.02),
), ),
onPressed: () {}, SizedBox(
child: const Text( width: 20.0,
'Overdued',
), ),
Row(
children: [Text("Overdue"), SwitchToOverdued()],
), ),
], ],
), ),
), )
], ],
), ),
], ],
...@@ -133,42 +120,64 @@ class _TopAppBar extends State<TopAppBar> { ...@@ -133,42 +120,64 @@ class _TopAppBar extends State<TopAppBar> {
floating: true, floating: true,
snap: true, snap: true,
); );
});
} }
} }
class LoanTypeFilter extends StatelessWidget implements PreferredSizeWidget { class SwitchToUpcoming extends StatefulWidget {
final double sizeAppBar = 50.0; const SwitchToUpcoming({super.key});
const LoanTypeFilter({super.key}); @override
State<SwitchToUpcoming> createState() => _SwitchToUpcoming();
}
class _SwitchToUpcoming extends State<SwitchToUpcoming> {
@override @override
Size get preferredSize => Size.fromHeight(sizeAppBar); Widget build(BuildContext context) {
return Consumer<AuthProvider>(builder: (context, authProvider, child) {
return Switch(
value: authProvider.filterByUpcoming,
onChanged: (bool value) {
setState(() {
authProvider.setFilterUpcoming();
if (authProvider.filterByUpcoming &&
authProvider.filterByOverdued) {
authProvider.setFilterOverdued();
}
authProvider.getMemberLoan();
});
},
);
});
}
}
class SwitchToOverdued extends StatefulWidget {
const SwitchToOverdued({super.key});
@override
State<SwitchToOverdued> createState() => _SwitchToOverdued();
}
class _SwitchToOverdued extends State<SwitchToOverdued> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Column( return Consumer<AuthProvider>(
children: [ builder: (context, authProvider, child) {
Padding( return Switch(
padding: const EdgeInsets.symmetric(horizontal: 20.0, vertical: 10.0), value: authProvider.filterByOverdued,
child: Row( onChanged: (bool value) {
mainAxisAlignment: MainAxisAlignment.center, setState(() {
children: [ authProvider.setFilterOverdued();
const SizedBox(width: 10.0), if (authProvider.filterByUpcoming &&
SizedBox( authProvider.filterByOverdued) {
height: 30.0, authProvider.setFilterUpcoming();
child: OutlinedButton( }
onPressed: () {}, child: const Text("Near Overdue")), authProvider.getMemberLoan();
), });
const SizedBox(width: 10.0), },
SizedBox( );
height: 30.0, },
child: OutlinedButton(
onPressed: () {}, child: const Text("Overdue")),
),
],
),
),
],
); );
} }
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment