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,97 +77,107 @@ class _TopAppBar extends State<TopAppBar> { ...@@ -78,97 +77,107 @@ 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: [ Row(
Row( crossAxisAlignment: CrossAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center, children: [
children: [ IconButton(
IconButton( onPressed: () {
onPressed: () { setState(() {
setState(() { showWidget = !showWidget;
showWidget = !showWidget; });
}); },
}, icon: Icon(
icon: Icon(showWidget ? Icons.close : Icons.filter_alt_outlined), showWidget ? Icons.close : Icons.filter_alt_outlined,
), ),
Offstage( ),
offstage: !showWidget, Offstage(
child: Row( offstage: !showWidget,
children: [ child: const Row(
FilledButton( mainAxisAlignment: MainAxisAlignment.spaceBetween,
style: TextButton.styleFrom( children: [
padding: EdgeInsets.symmetric( Row(
horizontal: screenSize.width * 0.04, children: [Text("Upcoming"), SwitchToUpcoming()],
vertical: screenSize.width * 0.02),
), ),
onPressed: () {}, SizedBox(
child: const Text('Near Outstanding'), width: 20.0,
),
const SizedBox(
width: 8.0,
),
FilledButton(
style: TextButton.styleFrom(
padding: EdgeInsets.symmetric(
horizontal: screenSize.width * 0.04,
vertical: screenSize.width * 0.02),
), ),
onPressed: () {}, Row(
child: const Text( children: [Text("Overdue"), SwitchToOverdued()],
'Overdued',
), ),
), ],
], ),
), )
), ],
], ),
), ],
], elevation: 10.0,
elevation: 10.0, automaticallyImplyLeading: false,
automaticallyImplyLeading: false, expandedHeight: 50,
expandedHeight: 50, 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});
@override
State<SwitchToUpcoming> createState() => _SwitchToUpcoming();
}
class _SwitchToUpcoming extends State<SwitchToUpcoming> {
@override
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();
});
},
);
});
}
}
const LoanTypeFilter({super.key}); class SwitchToOverdued extends StatefulWidget {
const SwitchToOverdued({super.key});
@override @override
Size get preferredSize => Size.fromHeight(sizeAppBar); 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