FBLA24/fbla_ui/lib/shared/export.dart

489 lines
18 KiB
Dart

import 'dart:io';
import 'package:fbla_ui/shared/api_logic.dart';
import 'package:fbla_ui/shared/utils.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:open_filex/open_filex.dart';
import 'package:path_provider/path_provider.dart';
import 'package:pdf/pdf.dart';
import 'package:pdf/widgets.dart' as pw;
import 'package:printing/printing.dart';
class _FilterBusinessDataTypeChips extends StatefulWidget {
final Set<DataTypeBusiness> selectedDataTypesBusiness;
const _FilterBusinessDataTypeChips({required this.selectedDataTypesBusiness});
@override
State<_FilterBusinessDataTypeChips> createState() =>
_FilterBusinessDataTypeChipsState();
}
class _FilterBusinessDataTypeChipsState
extends State<_FilterBusinessDataTypeChips> {
@override
Widget build(BuildContext context) {
List<Padding> chips = [];
for (var type in DataTypeBusiness.values) {
chips.add(Padding(
padding:
const EdgeInsets.only(left: 3.0, right: 3.0, bottom: 3.0, top: 3.0),
child: FilterChip(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
side:
BorderSide(color: Theme.of(context).colorScheme.secondary)),
label: Text(dataTypeFriendlyBusiness[type]!),
showCheckmark: false,
selected: widget.selectedDataTypesBusiness.contains(type),
onSelected: (bool selected) {
setState(() {
if (selected) {
widget.selectedDataTypesBusiness.add(type);
} else {
widget.selectedDataTypesBusiness.remove(type);
}
});
}),
));
}
return Wrap(
children: chips,
);
}
}
class _FilterJobDataTypeChips extends StatefulWidget {
final Set<DataTypeJob> selectedDataTypesJob;
const _FilterJobDataTypeChips({required this.selectedDataTypesJob});
@override
State<_FilterJobDataTypeChips> createState() =>
_FilterJobDataTypeChipsState();
}
class _FilterJobDataTypeChipsState extends State<_FilterJobDataTypeChips> {
@override
Widget build(BuildContext context) {
List<Padding> chips = [];
for (var type in DataTypeJob.values) {
chips.add(Padding(
padding:
const EdgeInsets.only(left: 3.0, right: 3.0, bottom: 3.0, top: 3.0),
child: FilterChip(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
side:
BorderSide(color: Theme.of(context).colorScheme.secondary)),
label: Text(dataTypeFriendlyJob[type]!),
showCheckmark: false,
selected: widget.selectedDataTypesJob.contains(type),
onSelected: (bool selected) {
setState(() {
if (selected) {
widget.selectedDataTypesJob.add(type);
} else {
widget.selectedDataTypesJob.remove(type);
}
});
}),
));
}
return Wrap(
children: chips,
);
}
}
Future<void> generatePDF(
{required BuildContext context,
required int documentTypeIndex,
Set<Business>? selectedBusinesses,
Set<Business>? selectedJobs}) async {
List<pw.Widget> headerColumns = [];
List<pw.TableRow> tableRows = [];
Set<DataTypeBusiness> dataTypesBusiness = {};
Set<DataTypeJob> dataTypesJob = {};
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: const Text('Export Settings'),
content: SizedBox(
width: 400,
height: 200,
child: Column(
children: [
const Padding(
padding: EdgeInsets.all(8.0),
child: Text('Data columns you want to export:'),
),
documentTypeIndex == 0
? _FilterBusinessDataTypeChips(
selectedDataTypesBusiness: dataTypesBusiness,
)
: _FilterJobDataTypeChips(
selectedDataTypesJob: dataTypesJob)
],
),
),
actions: [
TextButton(
child: const Text('Cancel'),
onPressed: () {
Navigator.of(context).pop();
}),
TextButton(
child: const Text('Generate'),
onPressed: () async {
if (documentTypeIndex == 0) {
List<Business> businesses = await fetchBusinesses(
selectedBusinesses!
.map((business) => business.id)
.toList());
if (dataTypesBusiness.isEmpty) {
dataTypesBusiness.addAll(DataTypeBusiness.values);
}
dataTypesBusiness =
sortDataTypesBusiness(dataTypesBusiness);
for (Business business in businesses) {
List<pw.Widget> businessRow = [];
if (dataTypesBusiness.contains(DataTypeBusiness.logo)) {
var apiLogo = await getLogo(business.id);
if (apiLogo.runtimeType != String) {
businessRow.add(pw.Padding(
child: pw.ClipRRect(
child: pw.Image(pw.MemoryImage(apiLogo),
height: 24, width: 24),
horizontalRadius: 4,
verticalRadius: 4),
padding: const pw.EdgeInsets.all(4.0)));
} else {
businessRow.add(pw.Padding(
child: pw.Icon(
getPwIconFromBusinessType(business.type!),
size: 24),
padding: const pw.EdgeInsets.all(4.0)));
}
}
for (DataTypeBusiness dataType in dataTypesBusiness) {
if (dataType != DataTypeBusiness.logo) {
var currentValue =
businessValueFromDataType(business, dataType);
if (currentValue != null) {
businessRow.add(pw.Padding(
child: pw.Text(businessValueFromDataType(
business, dataType)),
padding: const pw.EdgeInsets.all(4.0)));
} else {
businessRow.add(pw.Container());
}
}
}
tableRows.add(pw.TableRow(children: businessRow));
}
for (var filter in dataTypesBusiness) {
headerColumns.add(pw.Padding(
child: pw.Text(dataTypeFriendlyBusiness[filter]!,
style: const pw.TextStyle(fontSize: 10)),
padding: const pw.EdgeInsets.all(4.0)));
}
} else {
if (dataTypesJob.isEmpty) {
dataTypesJob.addAll(DataTypeJob.values);
}
dataTypesJob = sortDataTypesJob(dataTypesJob);
// List<Map<String, dynamic>> nameMapping =
// await fetchBusinessNames();
for (Business business in selectedJobs!) {
for (JobListing job in business.listings!) {
List<pw.Widget> jobRow = [];
for (DataTypeJob dataType in dataTypesJob) {
if (dataType != DataTypeJob.businessName) {
var currentValue =
jobValueFromDataType(job, dataType);
if (currentValue != null) {
jobRow.add(pw.Padding(
child: pw.Text(currentValue),
padding: const pw.EdgeInsets.all(4.0)));
} else {
jobRow.add(pw.Container());
}
} else {
jobRow.add(pw.Padding(
child: pw.Text(business.name!),
padding: const pw.EdgeInsets.all(4.0)));
}
}
tableRows.add(pw.TableRow(children: jobRow));
}
}
for (var filter in dataTypesJob) {
headerColumns.add(pw.Padding(
child: pw.Text(dataTypeFriendlyJob[filter]!,
style: const pw.TextStyle(fontSize: 10)),
padding: const pw.EdgeInsets.all(4.0)));
}
}
// Final Generation
DateTime dateTime = DateTime.now();
String minute = '00';
if (dateTime.minute.toString().length < 2) {
minute = '0${dateTime.minute}';
} else {
minute = dateTime.minute.toString();
}
String time = dateTime.hour <= 12
? '${dateTime.hour}:${minute}AM'
: '${dateTime.hour - 12}:${minute}PM';
String fileName =
'${documentTypeIndex == 0 ? 'Business' : 'Job Listing'} Data - ${dateTime.month}-${dateTime.day}-${dateTime.year} $time.pdf';
final pdf = pw.Document();
var svg = await rootBundle.loadString('assets/MarinoDev.svg');
var themeIcon = pw.ThemeData.withFont(
base: await PdfGoogleFonts.notoSansDisplayMedium(),
icons: await PdfGoogleFonts.materialIcons());
var finalTheme = themeIcon.copyWith(
defaultTextStyle: const pw.TextStyle(fontSize: 9),
);
pdf.addPage(pw.MultiPage(
theme: finalTheme,
pageFormat: PdfPageFormat.letter,
orientation: pw.PageOrientation.landscape,
margin: const pw.EdgeInsets.all(24),
build: (pw.Context context) {
return [
pw.Row(
mainAxisAlignment:
pw.MainAxisAlignment.spaceBetween,
children: [
pw.SvgImage(svg: svg, height: 40),
pw.Padding(
padding: const pw.EdgeInsets.all(8.0),
child: pw.Text(
'${documentTypeIndex == 0 ? 'Business' : 'Job Listing'} Datasheet',
style: pw.TextStyle(
fontSize: 32,
fontWeight: pw.FontWeight.bold)),
),
pw.Text(
'Generated on ${dateTime.month}/${dateTime.day}/${dateTime.year} at $time',
style: const pw.TextStyle(fontSize: 12),
textAlign: pw.TextAlign.right),
//
]),
pw.Table(
columnWidths: documentTypeIndex == 0
? _businessColumnSizes(dataTypesBusiness)
: _jobColumnSizes(dataTypesJob),
border: const pw.TableBorder(
bottom: pw.BorderSide(),
left: pw.BorderSide(),
right: pw.BorderSide(),
top: pw.BorderSide(),
horizontalInside: pw.BorderSide(),
verticalInside: pw.BorderSide()),
children: [
pw.TableRow(
decoration: const pw.BoxDecoration(
color: PdfColors.blue400),
children: headerColumns,
repeat: true,
),
...tableRows,
])
];
}));
Uint8List pdfBytes = await pdf.save();
if (kIsWeb) {
await Printing.sharePdf(
bytes: await pdf.save(),
filename: fileName,
);
} else {
var dir = await getTemporaryDirectory();
var tempDir = dir.path;
File pdfFile = File('$tempDir/$fileName');
pdfFile.writeAsBytesSync(pdfBytes);
OpenFilex.open(pdfFile.path);
}
Navigator.of(context).pop();
}),
],
);
});
}
Map<int, pw.TableColumnWidth> _businessColumnSizes(
Set<DataTypeBusiness> dataTypes) {
double space = 744.0;
List<DataTypeBusiness> sorted = sortDataTypesBusiness(dataTypes).toList();
Map<int, pw.TableColumnWidth> map = {};
if (sorted.contains(DataTypeBusiness.logo)) {
space -= 32;
map.addAll(
{sorted.indexOf(DataTypeBusiness.logo): const pw.FixedColumnWidth(32)});
}
if (dataTypes.contains(DataTypeBusiness.contactName)) {
space -= 72;
map.addAll({
sorted.indexOf(DataTypeBusiness.contactName):
const pw.FixedColumnWidth(72)
});
}
if (dataTypes.contains(DataTypeBusiness.contactPhone)) {
space -= 76;
map.addAll({
sorted.indexOf(DataTypeBusiness.contactPhone):
const pw.FixedColumnWidth(76)
});
}
double leftNum = 0;
if (dataTypes.contains(DataTypeBusiness.name)) {
leftNum += 1;
}
if (dataTypes.contains(DataTypeBusiness.website)) {
leftNum += 1;
}
if (dataTypes.contains(DataTypeBusiness.contactEmail)) {
leftNum += 1;
}
if (dataTypes.contains(DataTypeBusiness.notes)) {
leftNum += 2;
}
if (dataTypes.contains(DataTypeBusiness.description)) {
leftNum += 3;
}
leftNum = space / leftNum;
if (dataTypes.contains(DataTypeBusiness.name)) {
map.addAll(
{sorted.indexOf(DataTypeBusiness.name): pw.FixedColumnWidth(leftNum)});
}
if (dataTypes.contains(DataTypeBusiness.website)) {
map.addAll({
sorted.indexOf(DataTypeBusiness.website): pw.FixedColumnWidth(leftNum)
});
}
if (dataTypes.contains(DataTypeBusiness.contactEmail)) {
map.addAll({
sorted.indexOf(DataTypeBusiness.contactEmail):
pw.FixedColumnWidth(leftNum)
});
}
if (dataTypes.contains(DataTypeBusiness.notes)) {
map.addAll({
sorted.indexOf(DataTypeBusiness.notes): pw.FixedColumnWidth(leftNum * 2)
});
}
if (dataTypes.contains(DataTypeBusiness.description)) {
map.addAll({
sorted.indexOf(DataTypeBusiness.description):
pw.FixedColumnWidth(leftNum * 3)
});
}
return map;
}
Map<int, pw.TableColumnWidth> _jobColumnSizes(Set<DataTypeJob> dataTypes) {
Map<int, pw.TableColumnWidth> map = {};
List<DataTypeJob> sortedDataTypes = sortDataTypesJob(dataTypes).toList();
if (dataTypes.contains(DataTypeJob.businessName)) {
map.addAll({
sortedDataTypes.indexOf(sortedDataTypes
.where((element) => element == DataTypeJob.businessName)
.first): const pw.FractionColumnWidth(0.2)
});
}
if (dataTypes.contains(DataTypeJob.name)) {
map.addAll({
sortedDataTypes.indexOf(sortedDataTypes
.where((element) => element == DataTypeJob.name)
.first): const pw.FractionColumnWidth(0.2)
});
}
if (dataTypes.contains(DataTypeJob.description)) {
map.addAll({
sortedDataTypes.indexOf(sortedDataTypes
.where((element) => element == DataTypeJob.description)
.first): const pw.FractionColumnWidth(0.4)
});
}
if (dataTypes.contains(DataTypeJob.wage)) {
map.addAll({
sortedDataTypes.indexOf(sortedDataTypes
.where((element) => element == DataTypeJob.wage)
.first): const pw.FractionColumnWidth(0.15)
});
}
if (dataTypes.contains(DataTypeJob.link)) {
map.addAll({
sortedDataTypes.indexOf(sortedDataTypes
.where((element) => element == DataTypeJob.link)
.first): const pw.FractionColumnWidth(0.2)
});
}
return map;
}
dynamic businessValueFromDataType(
Business business, DataTypeBusiness dataType) {
switch (dataType) {
case DataTypeBusiness.name:
return business.name;
case DataTypeBusiness.description:
return business.description;
case DataTypeBusiness.type:
return business.type;
case DataTypeBusiness.website:
return business.website;
case DataTypeBusiness.contactName:
return business.contactName;
case DataTypeBusiness.contactEmail:
return business.contactEmail;
case DataTypeBusiness.contactPhone:
return business.contactPhone;
case DataTypeBusiness.notes:
return business.notes;
case DataTypeBusiness.logo:
return null;
}
}
dynamic jobValueFromDataType(JobListing job, DataTypeJob dataType) {
switch (dataType) {
case DataTypeJob.name:
return job.name;
case DataTypeJob.description:
return job.description;
case DataTypeJob.wage:
return job.wage;
case DataTypeJob.link:
return job.link;
case DataTypeJob.businessName:
return null;
}
}