FBLA24/fbla_ui/lib/shared/widgets.dart

410 lines
14 KiB
Dart

import 'package:dart_jsonwebtoken/dart_jsonwebtoken.dart';
import 'package:fbla_ui/pages/signin_page.dart';
import 'package:fbla_ui/shared/global_vars.dart';
import 'package:fbla_ui/shared/utils.dart';
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:url_launcher/url_launcher.dart';
class BusinessSearchBar extends StatefulWidget {
final String searchTextHint;
final Widget filterIconButton;
final void Function(String) setSearchCallback;
const BusinessSearchBar(
{super.key,
required this.setSearchCallback,
required this.searchTextHint,
required this.filterIconButton});
@override
State<BusinessSearchBar> createState() => _BusinessSearchBarState();
}
class _BusinessSearchBarState extends State<BusinessSearchBar> {
TextEditingController controller = TextEditingController();
@override
Widget build(BuildContext context) {
return SizedBox(
width: 450,
height: 50,
child: SearchBar(
hintText: widget.searchTextHint,
controller: controller,
backgroundColor: WidgetStateProperty.resolveWith((notNeeded) {
return Theme.of(context).colorScheme.surfaceContainer;
}),
onChanged: (query) {
widget.setSearchCallback(query);
},
leading: const Padding(
padding: EdgeInsets.only(left: 8.0),
child: Icon(Icons.search),
),
trailing: [
if (controller.text != '')
IconButton(
icon: const Icon(Icons.clear),
onPressed: () {
controller.text = '';
widget.setSearchCallback('');
},
),
widget.filterIconButton
]),
);
}
}
class FilterChips extends StatefulWidget {
final Set<JobType>? selectedJobChips;
final Set<BusinessType>? selectedBusinessChips;
const FilterChips(
{super.key, this.selectedJobChips, this.selectedBusinessChips});
@override
State<FilterChips> createState() => _FilterChipsState();
}
class _FilterChipsState extends State<FilterChips> {
List<Padding> filterChips() {
List<Padding> chips = [];
if (widget.selectedJobChips != null) {
for (var type in JobType.values) {
chips.add(Padding(
padding: const EdgeInsets.only(left: 4.0, right: 4.0),
child: FilterChip(
showCheckmark: false,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20)),
label: Text(getNameFromJobType(type)),
selected: widget.selectedJobChips!.contains(type),
onSelected: (bool selected) {
setState(() {
if (selected) {
widget.selectedJobChips!.add(type);
} else {
widget.selectedJobChips!.remove(type);
}
});
}),
));
}
} else if (widget.selectedBusinessChips != null) {
for (var type in BusinessType.values) {
chips.add(Padding(
padding: const EdgeInsets.only(left: 4.0, right: 4.0),
child: FilterChip(
showCheckmark: false,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20)),
label: Text(getNameFromBusinessType(type)),
selected: widget.selectedBusinessChips!.contains(type),
onSelected: (bool selected) {
setState(() {
if (selected) {
widget.selectedBusinessChips!.add(type);
} else {
widget.selectedBusinessChips!.remove(type);
}
});
}),
));
}
}
return chips;
}
@override
Widget build(BuildContext context) {
return Wrap(
children: filterChips(),
);
}
}
class MainSliverAppBar extends StatefulWidget {
final bool widescreen;
final Widget filterIconButton;
final void Function(String) setSearch;
final void Function() themeCallback;
final void Function() generatePDF;
final void Function(bool) updateLoggedIn;
final String searchHintText;
const MainSliverAppBar({
super.key,
required this.widescreen,
required this.setSearch,
required this.searchHintText,
required this.themeCallback,
required this.filterIconButton,
required this.updateLoggedIn,
required this.generatePDF,
});
@override
State<MainSliverAppBar> createState() => _MainSliverAppBarState();
}
class _MainSliverAppBarState extends State<MainSliverAppBar> {
@override
Widget build(BuildContext context) {
return SliverAppBar(
title: widget.widescreen
? Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Flexible(
child: BusinessSearchBar(
setSearchCallback: widget.setSearch,
searchTextHint: widget.searchHintText,
filterIconButton: widget.filterIconButton,
),
)
// const PreferredSize(
// preferredSize: Size(144, 0), child: SizedBox())
],
)
: const Text('Job Link'),
toolbarHeight: 70,
stretch: false,
backgroundColor: Theme.of(context).colorScheme.surface,
pinned: true,
// floating: true,
scrolledUnderElevation: 0,
centerTitle: !widget.widescreen,
expandedHeight: widget.widescreen ? 70 : 120,
bottom: _getBottom(widget.widescreen),
leading: !widget.widescreen
? IconButton(
icon: Icon(getIconFromThemeMode(themeMode)),
onPressed: () {
setState(() {
widget.themeCallback();
});
},
)
: null,
actions: [
IconButton(
icon: const Icon(Icons.file_download_outlined),
onPressed: widget.generatePDF,
),
IconButton(
icon: const Icon(Icons.help),
onPressed: () {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: const Text('About'),
backgroundColor: Theme.of(context).colorScheme.surface,
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();
}),
],
);
});
},
),
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.surface,
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', '');
widget.updateLoggedIn(false);
Navigator.of(context).pop();
}),
],
);
});
} else {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
SignInPage(refreshAccount: widget.updateLoggedIn)));
}
},
),
),
],
);
}
PreferredSizeWidget? _getBottom(bool widescreen) {
if (!widescreen) {
return PreferredSize(
preferredSize: const Size.fromHeight(0),
child: SizedBox(
height: 70,
child: Padding(
padding: const EdgeInsets.all(10),
child: BusinessSearchBar(
filterIconButton: widget.filterIconButton,
setSearchCallback: widget.setSearch,
searchTextHint: widget.searchHintText,
),
),
),
);
}
return null;
}
}
class ContactInformationCard extends StatelessWidget {
final Business business;
ContactInformationCard({super.key, required this.business});
@override
Widget build(BuildContext context) {
return Card(
clipBehavior: Clip.antiAlias,
child: Column(
children: [
Row(
children: [
Padding(
padding: const EdgeInsets.only(left: 16.0, top: 8.0),
child: Text(
business.contactName!,
textAlign: TextAlign.left,
style: const TextStyle(
fontSize: 20, fontWeight: FontWeight.bold),
),
),
],
),
if (business.contactPhone != null)
ListTile(
leading: const Icon(Icons.phone),
title: Text(business.contactPhone!),
onTap: () {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
backgroundColor: Theme.of(context).colorScheme.surface,
title: Text('Contact ${business.contactName}'),
content: Text(
'Would you like to call or text ${business.contactName}?'),
actions: [
TextButton(
child: const Text('Text'),
onPressed: () {
launchUrl(
Uri.parse('sms:${business.contactPhone}'));
Navigator.of(context).pop();
}),
TextButton(
child: const Text('Call'),
onPressed: () async {
launchUrl(
Uri.parse('tel:${business.contactPhone}'));
Navigator.of(context).pop();
}),
],
);
});
},
),
if (business.contactEmail != null)
ListTile(
leading: const Icon(Icons.email),
title: Text(business.contactEmail!),
onTap: () {
launchUrl(Uri.parse('mailto:${business.contactEmail}'));
},
),
],
),
);
}
}