FBLA24/fbla_ui/lib/home.dart

445 lines
18 KiB
Dart

import 'package:dart_jsonwebtoken/dart_jsonwebtoken.dart';
import 'package:fbla_ui/api_logic.dart';
import 'package:fbla_ui/main.dart';
import 'package:fbla_ui/pages/create_edit_business.dart';
import 'package:fbla_ui/pages/export_data.dart';
import 'package:fbla_ui/pages/signin_page.dart';
import 'package:fbla_ui/shared.dart';
import 'package:flutter/material.dart';
import 'package:rive/rive.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:url_launcher/url_launcher.dart';
typedef Callback = void Function();
class Home extends StatefulWidget {
final Callback themeCallback;
const Home({super.key, required this.themeCallback});
@override
State<Home> createState() => _HomeState();
}
class _HomeState extends State<Home> {
late Future refreshBusinessDataFuture;
bool _isPreviousData = false;
late List<Business> businesses;
@override
void initState() {
super.initState();
refreshBusinessDataFuture = fetchBusinessData();
initialLogin();
}
Future<void> initialLogin() async {
final prefs = await SharedPreferences.getInstance();
bool? rememberMe = prefs.getBool('rememberMe');
if (rememberMe != null && rememberMe) {
String? username = prefs.getString('username');
String? password = prefs.getString('password');
jwt = await signIn(username!, password!);
if (!jwt.contains('Error:')) {
setState(() {
loggedIn = true;
});
}
}
}
void setStateCallback() {
setState(() {
loggedIn = loggedIn;
});
}
@override
Widget build(BuildContext context) {
bool widescreen = MediaQuery.sizeOf(context).width >= 1000;
return Scaffold(
floatingActionButton: _getFAB(),
body: RefreshIndicator(
edgeOffset: 120,
onRefresh: () async {
var refreshedData = fetchBusinessData();
await refreshedData;
setState(() {
refreshBusinessDataFuture = refreshedData;
});
},
child: CustomScrollView(
slivers: [
SliverAppBar(
title: widescreen ? _searchBar() : const Text('Job Link'),
toolbarHeight: 70,
pinned: true,
scrolledUnderElevation: 0,
centerTitle: true,
expandedHeight: widescreen ? 70 : 120,
backgroundColor: Theme.of(context).colorScheme.background,
bottom: _getBottom(),
leading: IconButton(
icon: getIconFromThemeMode(themeMode),
onPressed: () {
setState(() {
widget.themeCallback();
});
},
),
actions: [
IconButton(
icon: const Icon(Icons.help),
onPressed: () {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: const Text('About'),
backgroundColor:
Theme.of(context).colorScheme.background,
content: SizedBox(
width: 500,
child: IntrinsicHeight(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'Welcome to my FBLA 2024 Coding and Programming submission!\n\n'
'MarinoDev Job Link aims to provide comprehensive details of businesses and community partners'
' for Waukesha West High School\'s Career and Technical Education Department.\n\n'),
MouseRegion(
cursor: SystemMouseCursors.click,
child: GestureDetector(
child: const Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Text('Git Repo:'),
Text(
'https://git.marinodev.com/MarinoDev/FBLA24\n',
style: TextStyle(
color: Colors.blue)),
],
),
onTap: () {
launchUrl(Uri.https(
'git.marinodev.com',
'/MarinoDev/FBLA24'));
},
),
),
MouseRegion(
cursor: SystemMouseCursors.click,
child: GestureDetector(
child: const Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Text(
'Please direct any questions to'),
Text('drake@marinodev.com',
style: TextStyle(
color: Colors.blue)),
],
),
onTap: () {
launchUrl(Uri.parse(
'mailto:drake@marinodev.com'));
},
),
)
],
),
),
),
actions: [
TextButton(
child: const Text('OK'),
onPressed: () {
Navigator.of(context).pop();
}),
],
);
});
},
),
IconButton(
icon: const Icon(Icons.picture_as_pdf),
onPressed: () async {
if (!_isPreviousData) {
ScaffoldMessenger.of(context).clearSnackBars();
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
width: 300,
behavior: SnackBarBehavior.floating,
content: Text('There is no data!'),
duration: Duration(seconds: 2),
),
);
} else {
selectedDataTypes = <DataType>{};
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
ExportData(businesses: businesses)));
}
},
),
Padding(
padding: const EdgeInsets.only(right: 8.0),
child: IconButton(
icon: loggedIn
? const Icon(Icons.account_circle)
: const Icon(Icons.login),
onPressed: () {
if (loggedIn) {
var payload = JWT.decode(jwt).payload;
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
backgroundColor:
Theme.of(context).colorScheme.background,
title: Text('Hi, ${payload['username']}!'),
content: Text(
'You are logged in as an admin with username ${payload['username']}.'),
actions: [
TextButton(
child: const Text('Cancel'),
onPressed: () {
Navigator.of(context).pop();
}),
TextButton(
child: const Text('Logout'),
onPressed: () async {
final prefs = await SharedPreferences
.getInstance();
prefs.setBool('rememberMe', false);
prefs.setString('username', '');
prefs.setString('password', '');
setState(() {
loggedIn = false;
});
Navigator.of(context).pop();
}),
],
);
});
} else {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SignInPage(
refreshAccount: setStateCallback)));
}
},
),
),
],
),
FutureBuilder(
future: refreshBusinessDataFuture,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
if (snapshot.hasData) {
if (snapshot.data.runtimeType == String) {
_isPreviousData = false;
return SliverToBoxAdapter(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(children: [
Center(
child: Text(snapshot.data,
textAlign: TextAlign.center)),
Padding(
padding: const EdgeInsets.all(8.0),
child: FilledButton(
child: const Text('Retry'),
onPressed: () {
var refreshedData = fetchBusinessData();
setState(() {
refreshBusinessDataFuture = refreshedData;
});
},
),
),
]),
));
}
businesses = snapshot.data;
_isPreviousData = true;
return BusinessDisplayPanel(
businesses: businesses,
widescreen: widescreen,
selectable: false);
} else if (snapshot.hasError) {
return SliverToBoxAdapter(
child: Padding(
padding: const EdgeInsets.only(left: 16.0, right: 16.0),
child: Text(
'Error when loading data! Error: ${snapshot.error}'),
));
}
} else if (snapshot.connectionState ==
ConnectionState.waiting) {
if (_isPreviousData) {
return BusinessDisplayPanel(
businesses: businesses,
widescreen: widescreen,
selectable: false);
} else {
return SliverToBoxAdapter(
child: Container(
padding: const EdgeInsets.all(8.0),
alignment: Alignment.center,
// child: const CircularProgressIndicator(),
child: const SizedBox(
width: 75,
height: 75,
child: RiveAnimation.asset(
'assets/mdev_triangle_loading.riv'),
// child: RiveAnimation.file(
// 'assets/mdev_triangle_loading.riv',
// ),
),
));
}
}
return SliverToBoxAdapter(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
'\nError: ${snapshot.error}',
style: const TextStyle(fontSize: 18),
textAlign: TextAlign.center,
),
),
);
}),
],
),
),
);
}
Widget? _getFAB() {
if (loggedIn) {
return FloatingActionButton(
child: const Icon(Icons.add),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const CreateEditBusiness()));
},
);
}
return null;
}
Widget _searchBar() {
return SizedBox(
width: 800,
height: 50,
child: TextField(
onChanged: (query) {
setState(() {
searchFilter = query;
});
},
decoration: InputDecoration(
labelText: 'Search',
hintText: 'Search',
prefixIcon: const Padding(
padding: EdgeInsets.only(left: 8.0),
child: Icon(Icons.search),
),
border: const OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(25.0)),
),
suffixIcon: Padding(
padding: const EdgeInsets.only(right: 8.0),
child: IconButton(
tooltip: 'Filters',
icon: Icon(Icons.filter_list,
color: isFiltered
? Theme.of(context).colorScheme.primary
: Theme.of(context).colorScheme.onBackground),
onPressed: () {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
// DO NOT MOVE TO SEPARATE WIDGET, setState is needed in main tree
backgroundColor:
Theme.of(context).colorScheme.background,
title: const Text('Filter Options'),
content: const FilterChips(),
actions: [
TextButton(
child: const Text('Reset'),
onPressed: () {
setState(() {
filters = <BusinessType>{};
selectedChips = <BusinessType>{};
isFiltered = false;
});
Navigator.of(context).pop();
}),
TextButton(
child: const Text('Cancel'),
onPressed: () {
selectedChips = Set.from(filters);
Navigator.of(context).pop();
}),
TextButton(
child: const Text('Apply'),
onPressed: () {
setState(() {
filters = Set.from(selectedChips);
if (filters.isNotEmpty) {
isFiltered = true;
} else {
isFiltered = false;
}
});
Navigator.of(context).pop();
}),
],
);
});
},
),
),
),
),
);
}
PreferredSizeWidget? _getBottom() {
if (MediaQuery.sizeOf(context).width <= 1000) {
return PreferredSize(
preferredSize: const Size.fromHeight(0),
child: SizedBox(
// color: Theme.of(context).colorScheme.background,
height: 70,
child: Padding(
padding: const EdgeInsets.all(10),
child: _searchBar(),
),
),
);
}
return null;
}
}