diff --git a/imports/pages/give/history/Layout.js b/imports/pages/give/history/Layout.js
index 14f684765..30c173023 100644
--- a/imports/pages/give/history/Layout.js
+++ b/imports/pages/give/history/Layout.js
@@ -61,6 +61,8 @@ type ILayout = {
reloading: boolean,
family: [Object],
filterTransactions: Function,
+ onPrintClick: Function,
+ printLoading: boolean,
};
export default ({
@@ -71,6 +73,8 @@ export default ({
reloading,
filterTransactions,
family,
+ onPrintClick,
+ printLoading,
}: ILayout) => (
@@ -90,6 +94,28 @@ export default ({
)}
+ {transactions.length > 0 && (
+
+ {!printLoading && (
+
+
+
+ )}
+
+ {printLoading && (
+
+
+
+ )}
+
+ )}
+
{!transactions.length && ready && !reloading && (
@@ -117,5 +143,40 @@ export default ({
)}
{ready && transactions.length && !reloading && !done && }
+
+ {/* Print Button */}
+
+
+ {!printLoading && (
+
+
+
+ )}
+
+ {printLoading && (
+
+
+
+ )}
+
+
+
);
diff --git a/imports/pages/give/history/__tests__/__snapshots__/Layout.js.snap b/imports/pages/give/history/__tests__/__snapshots__/Layout.js.snap
index 0417b2899..07232c899 100644
--- a/imports/pages/give/history/__tests__/__snapshots__/Layout.js.snap
+++ b/imports/pages/give/history/__tests__/__snapshots__/Layout.js.snap
@@ -35,6 +35,40 @@ exports[`Layout renders no transactions if there are none and ready 1`] = `
+
`;
@@ -61,6 +95,40 @@ exports[`Layout renders reloading if no transactions and not ready 1`] = `
+
`;
@@ -82,6 +150,30 @@ exports[`Layout renders reloading version 1`] = `
}
} />
+
+
`;
@@ -127,6 +253,30 @@ exports[`Layout renders with negative transactions 1`] = `
family={Array []} />
+
`;
@@ -170,6 +354,30 @@ exports[`Layout renders with props 1`] = `
family={Array []} />
+
`;
diff --git a/imports/pages/give/history/__tests__/__snapshots__/index.js.snap b/imports/pages/give/history/__tests__/__snapshots__/index.js.snap
index 828ac0f55..4262abc87 100644
--- a/imports/pages/give/history/__tests__/__snapshots__/index.js.snap
+++ b/imports/pages/give/history/__tests__/__snapshots__/index.js.snap
@@ -5,6 +5,8 @@ exports[`test renders with props 1`] = `
done={false}
family={Array []}
filterTransactions={[Function]}
+ onPrintClick={[Function]}
+ printLoading={false}
ready={true}
reloading={false}
transactions={Array []} />
diff --git a/imports/pages/give/history/index.js b/imports/pages/give/history/index.js
index 215f72ff3..a63ea9ce3 100644
--- a/imports/pages/give/history/index.js
+++ b/imports/pages/give/history/index.js
@@ -1,11 +1,14 @@
// @flow
import { Component, PropTypes } from "react";
+import moment from "moment";
import { graphql } from "react-apollo";
import gql from "graphql-tag";
+import fileSaver from "file-saver";
import infiniteScroll from "../../../decorators/infiniteScroll";
import Authorized from "../../../blocks/authorzied";
+import base64ToBlob from "../../../util/base64ToBlob";
import Layout from "./Layout";
@@ -21,9 +24,11 @@ class TemplateWithoutData extends Component {
family: PropTypes.array, // eslint-disable-line
}),
setRightProps: PropTypes.func,
+ currentVariables: PropTypes.obj,
+ getPDF: PropTypes.func,
}
- state = { refetching: false }
+ state = { refetching: false, printLoading: false }
componentWillMount() {
this.props.setRightProps({
@@ -39,6 +44,21 @@ class TemplateWithoutData extends Component {
});
}
+ onPrintClick = (e) => {
+ e.preventDefault();
+
+ this.setState({ printLoading: true });
+ this.props.getPDF(this.props.currentVariables)
+ .then(({ data: { transactionStatement } }) => {
+ const blob = base64ToBlob(transactionStatement.file);
+ this.setState({ printLoading: false });
+ fileSaver.saveAs(blob, `${moment().year()} NewSpring Church Giving Summary`);
+ })
+ .catch(() => {
+ this.setState({ printLoading: false });
+ });
+ }
+
render() {
const {
transactions,
@@ -49,6 +69,8 @@ class TemplateWithoutData extends Component {
filterTransactions,
} = this.props;
+ const { printLoading } = this.state;
+
return (
);
}
@@ -71,8 +95,27 @@ const FILTER_QUERY = gql`
}
}
`;
+
const withFilter = graphql(FILTER_QUERY, { name: "filter" });
+const GET_STATEMENT = gql`
+ mutation GetGivingStatement($limit: Int, $skip: Int, $people: [Int], $start: String, $end: String) {
+ transactionStatement(
+ limit: $limit,
+ skip: $skip,
+ people: $people,
+ start: $start,
+ end: $end
+ ){
+ file
+ }
+ }
+`;
+
+const withStatement = graphql(GET_STATEMENT, {
+ props: ({ mutate }) => ({ getPDF: (variables) => mutate({ variables }) }),
+});
+
const TRANSACTIONS_QUERY = gql`
query GetTransactions($limit: Int, $skip: Int, $people: [Int], $start: String, $end: String) {
transactions(
@@ -106,6 +149,7 @@ const withTransactions = graphql(TRANSACTIONS_QUERY, {
ssr: false,
},
props: ({ data }) => ({
+ currentVariables: data.variables,
transactions: data.transactions || [],
loading: data.loading,
done: (
@@ -132,9 +176,11 @@ const withTransactions = graphql(TRANSACTIONS_QUERY, {
});
const Template = withFilter(
- withTransactions(
- infiniteScroll()(
- TemplateWithoutData
+ withStatement(
+ withTransactions(
+ infiniteScroll()(
+ TemplateWithoutData
+ )
)
)
);
diff --git a/imports/util/base64ToBlob.js b/imports/util/base64ToBlob.js
new file mode 100644
index 000000000..1f7f31785
--- /dev/null
+++ b/imports/util/base64ToBlob.js
@@ -0,0 +1,20 @@
+
+const decodeBase64 = (string) => atob(string);
+
+const getLength = (value) => value.length;
+
+const buildByteArray = (string, stringLength) => {
+ const buffer = new ArrayBuffer(stringLength);
+ const array = new Uint8Array(buffer);
+ for (let i = 0; i < stringLength; i += 1) { array[i] = string.charCodeAt(i); }
+ return array;
+};
+
+const createBlob = (byteArray) => new Blob([byteArray], { type: "application/pdf" });
+
+export default (base64String) => {
+ const decodedString = decodeBase64(base64String);
+ const decodedStringLength = getLength(decodedString);
+ const byteArray = buildByteArray(decodedString, decodedStringLength);
+ return byteArray ? createBlob(byteArray) : null;
+};
diff --git a/package.json b/package.json
index b514f800a..0b3d210e5 100644
--- a/package.json
+++ b/package.json
@@ -97,6 +97,7 @@
"cheerio": "^0.20.0",
"compression": "^1.6.2",
"cookie-parser": "^1.4.3",
+ "file-saver": "^1.3.3",
"fibers": "^1.0.15",
"google-map-react": "^0.14.8",
"graphql-tag": "^0.1.8",
diff --git a/stylesheets/icons.css b/stylesheets/icons.css
index 6b943a64d..d41ba7b51 100644
--- a/stylesheets/icons.css
+++ b/stylesheets/icons.css
@@ -141,6 +141,9 @@
.icon-groups:before {
content: "\41";
}
+.icon-print:before {
+ content: "\43";
+}
.icon-lock:before {
content: "\4b";
}
diff --git a/yarn.lock b/yarn.lock
index cf37a805c..b9b9faa09 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -3143,6 +3143,10 @@ file-loader@^0.9.0:
dependencies:
loader-utils "~0.2.5"
+file-saver@^1.3.3:
+ version "1.3.3"
+ resolved "https://registry.yarnpkg.com/file-saver/-/file-saver-1.3.3.tgz#cdd4c44d3aa264eac2f68ec165bc791c34af1232"
+
filename-regex@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/filename-regex/-/filename-regex-2.0.0.tgz#996e3e80479b98b9897f15a8a58b3d084e926775"