FBLA24/fbla_ui/lib/pages/business_detail.dart
2024-06-26 20:33:05 -05:00

474 lines
18 KiB
Dart

import 'package:fbla_ui/main.dart';
import 'package:fbla_ui/pages/create_edit_business.dart';
import 'package:fbla_ui/pages/create_edit_listing.dart';
import 'package:fbla_ui/pages/listing_detail.dart';
import 'package:fbla_ui/shared/api_logic.dart';
import 'package:fbla_ui/shared/global_vars.dart';
import 'package:flutter/material.dart';
import 'package:rive/rive.dart';
import 'package:url_launcher/url_launcher.dart';
import '../shared/utils.dart';
class BusinessDetail extends StatefulWidget {
final int id;
final String name;
const BusinessDetail({super.key, required this.id, required this.name});
@override
State<BusinessDetail> createState() => _CreateBusinessDetailState();
}
class _CreateBusinessDetailState extends State<BusinessDetail> {
late Future loadBusiness;
bool _isRetrying = false;
@override
void initState() {
super.initState();
loadBusiness = fetchBusiness(widget.id);
}
@override
Widget build(BuildContext context) {
return FutureBuilder(
future: loadBusiness,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
if (snapshot.hasData) {
if (snapshot.data.runtimeType != String) {
return Scaffold(
appBar: AppBar(
title: Text(snapshot.data.name),
actions: _getActions(snapshot.data),
),
body: _detailBody(snapshot.data),
);
} else {
return Scaffold(
appBar: AppBar(
title: Text(widget.name),
),
body: 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: () async {
if (!_isRetrying) {
setState(() {
_isRetrying = true;
});
var refreshedData =
await fetchBusiness(widget.id);
setState(() {
loadBusiness = refreshedData;
});
}
},
),
),
]),
),
);
}
}
} else if (snapshot.connectionState == ConnectionState.waiting) {
return Scaffold(
appBar: AppBar(
title: Text(widget.name),
),
body: Container(
padding: const EdgeInsets.all(8.0),
alignment: Alignment.center,
child: const SizedBox(
width: 75,
height: 75,
child:
RiveAnimation.asset('assets/mdev_triangle_loading.riv'),
)),
);
}
return Padding(
padding: const EdgeInsets.all(8.0),
child: Scaffold(
appBar: AppBar(
title: Text(widget.name),
),
body: Text(
'\nError: ${snapshot.error}',
style: const TextStyle(fontSize: 18),
textAlign: TextAlign.center,
),
),
);
});
}
Widget _detailBody(Business business) {
return ListView(
children: [
// Title, logo, desc, website
Center(
child: SizedBox(
width: 800,
child: Column(
children: [
Padding(
padding: const EdgeInsets.only(top: 4.0),
child: Card(
clipBehavior: Clip.antiAlias,
child: Column(
children: [
Padding(
padding: const EdgeInsets.only(right: 8.0),
child: ListTile(
titleAlignment: ListTileTitleAlignment.titleHeight,
title: Text(business.name!,
style: const TextStyle(
fontSize: 24, fontWeight: FontWeight.bold)),
subtitle: Text(
business.description!,
),
contentPadding:
const EdgeInsets.only(bottom: 8, left: 16),
leading: ClipRRect(
borderRadius: BorderRadius.circular(6.0),
child: Image.network(
'$apiAddress/logos/${business.id}',
width: 48,
height: 48, errorBuilder:
(BuildContext context, Object exception,
StackTrace? stackTrace) {
return Icon(
getIconFromBusinessType(
business.type ?? BusinessType.other),
size: 48);
}),
),
),
),
if (business.website != null)
ListTile(
leading: const Icon(Icons.link),
title: const Text('Website'),
subtitle: Text(
business.website!
.replaceAll('https://', '')
.replaceAll('http://', '')
.replaceAll('www.', ''),
style: const TextStyle(color: Colors.blue)),
onTap: () {
launchUrl(Uri.parse(business.website!));
},
),
],
),
),
),
// Available positions
if (business.listings != null || loggedIn)
Card(
clipBehavior: Clip.antiAlias,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.only(left: 16, top: 4),
child: _GetListingsTitle(business)),
if (business.listings != null)
_JobList(business: business)
else
Padding(
padding: const EdgeInsets.only(bottom: 8.0),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const Padding(
padding: EdgeInsets.only(left: 16.0),
child: Text('No job listings exist.'),
),
TextButton(
child: const Text('add one?'),
onPressed: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) =>
CreateEditJobListing(
inputBusiness: business,
)));
},
)
],
),
)
]),
),
// Contact info
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}'));
},
),
],
),
),
// Location
Card(
clipBehavior: Clip.antiAlias,
child: ListTile(
leading: const Icon(Icons.location_on),
title: Text(business.locationName),
subtitle: Text(business.locationAddress!),
onTap: () {
launchUrl(Uri.parse(Uri.encodeFull(
'https://www.google.com/maps/search/?api=1&query=${business.locationName} ${business.locationAddress}')));
},
),
),
// Notes
if (business.notes != null && business.notes != '')
Card(
child: ListTile(
leading: const Icon(Icons.notes),
title: const Text(
'Additional Notes',
style: TextStyle(
fontSize: 20, fontWeight: FontWeight.bold),
),
subtitle: Text(business.notes!),
),
),
],
),
),
),
],
);
}
List<Widget>? _getActions(Business business) {
if (loggedIn) {
return [
IconButton(
icon: const Icon(Icons.edit),
onPressed: () {
Navigator.of(context).push(MaterialPageRoute(
builder: (context) => CreateEditBusiness(
inputBusiness: business,
)));
},
),
IconButton(
icon: const Icon(Icons.delete),
onPressed: () {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
backgroundColor: Theme.of(context).colorScheme.surface,
title: const Text('Are You Sure?'),
content:
Text('This will permanently delete ${business.name}.'),
actions: [
TextButton(
child: const Text('Cancel'),
onPressed: () {
Navigator.of(context).pop();
}),
TextButton(
child: const Text('Yes'),
onPressed: () async {
String? deleteResult =
await deleteBusiness(business.id);
if (deleteResult != null) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
width: 300,
behavior: SnackBarBehavior.floating,
content: Text(deleteResult)));
} else {
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (context) => const MainApp()));
}
}),
],
);
});
},
),
];
}
return null;
}
}
class _JobList extends StatelessWidget {
final Business business;
const _JobList({required this.business});
@override
Widget build(BuildContext context) {
List<_JobListItem> listItems = [];
for (JobListing listing in business.listings!) {
listItems.add(_JobListItem(
jobListing: listing,
fromBusiness: business,
));
}
return ListView(
shrinkWrap: true,
children: listItems,
);
}
}
class _JobListItem extends StatelessWidget {
final JobListing jobListing;
final Business fromBusiness;
const _JobListItem({required this.jobListing, required this.fromBusiness});
@override
Widget build(BuildContext context) {
return ListTile(
leading: Icon(getIconFromJobType(jobListing.type!)),
title: Text(jobListing.name),
subtitle: Text(
jobListing.description,
style: const TextStyle(overflow: TextOverflow.ellipsis),
),
trailing: _getEditIcon(context, fromBusiness, jobListing),
onTap: () {
Navigator.of(context).push(MaterialPageRoute(
builder: (context) => JobListingDetail(
listing: jobListing,
fromBusiness: fromBusiness,
)));
},
);
}
Widget? _getEditIcon(
BuildContext context, Business fromBusiness, JobListing inputListing) {
if (loggedIn) {
return IconButton(
icon: const Icon(
Icons.edit,
),
onPressed: () {
Navigator.of(context).push(MaterialPageRoute(
builder: (context) => CreateEditJobListing(
inputBusiness: fromBusiness,
inputJobListing: inputListing,
)));
},
);
}
return null;
}
}
class _GetListingsTitle extends StatelessWidget {
final Business fromBusiness;
const _GetListingsTitle(this.fromBusiness);
@override
Widget build(BuildContext context) {
if (!loggedIn) {
return Text(
'Available Postitions (${fromBusiness.listings?.length ?? '0'})',
style: const TextStyle(fontSize: 20, fontWeight: FontWeight.bold));
} else {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('Available Postitions (${fromBusiness.listings?.length ?? '0'})',
style:
const TextStyle(fontSize: 20, fontWeight: FontWeight.bold)),
Padding(
padding: const EdgeInsets.only(right: 24.0),
child: IconButton(
icon: const Icon(Icons.add),
onPressed: () {
Navigator.of(context).push(MaterialPageRoute(
builder: (context) => CreateEditJobListing(
inputBusiness: fromBusiness,
)));
},
),
)
],
);
}
}
}