@churchapps/apphelper 0.0.10 → 0.0.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (191) hide show
  1. package/.eslintignore +3 -3
  2. package/.eslintrc.json +22 -22
  3. package/LICENSE +21 -21
  4. package/README.md +16 -16
  5. package/dist/components/markdownEditor/editor.css +787 -787
  6. package/dist/components/markdownEditor/images/icons/arrow-clockwise.svg +3 -3
  7. package/dist/components/markdownEditor/images/icons/arrow-counterclockwise.svg +3 -3
  8. package/dist/components/markdownEditor/images/icons/chat-square-quote.svg +3 -3
  9. package/dist/components/markdownEditor/images/icons/chevron-down.svg +2 -2
  10. package/dist/components/markdownEditor/images/icons/code.svg +2 -2
  11. package/dist/components/markdownEditor/images/icons/journal-code.svg +4 -4
  12. package/dist/components/markdownEditor/images/icons/journal-text.svg +4 -4
  13. package/dist/components/markdownEditor/images/icons/justify.svg +2 -2
  14. package/dist/components/markdownEditor/images/icons/link.svg +3 -3
  15. package/dist/components/markdownEditor/images/icons/list-ol.svg +3 -3
  16. package/dist/components/markdownEditor/images/icons/list-ul.svg +2 -2
  17. package/dist/components/markdownEditor/images/icons/pencil-fill.svg +2 -2
  18. package/dist/components/markdownEditor/images/icons/text-center.svg +2 -2
  19. package/dist/components/markdownEditor/images/icons/text-left.svg +2 -2
  20. package/dist/components/markdownEditor/images/icons/text-paragraph.svg +2 -2
  21. package/dist/components/markdownEditor/images/icons/text-right.svg +2 -2
  22. package/dist/components/markdownEditor/images/icons/type-bold.svg +2 -2
  23. package/dist/components/markdownEditor/images/icons/type-h1.svg +2 -2
  24. package/dist/components/markdownEditor/images/icons/type-h2.svg +2 -2
  25. package/dist/components/markdownEditor/images/icons/type-h3.svg +2 -2
  26. package/dist/components/markdownEditor/images/icons/type-h4.svg +12 -12
  27. package/dist/components/markdownEditor/images/icons/type-italic.svg +2 -2
  28. package/dist/components/markdownEditor/images/icons/type-strikethrough.svg +2 -2
  29. package/dist/components/markdownEditor/images/icons/type-underline.svg +2 -2
  30. package/dist/components/material/SiteWrapper.d.ts.map +1 -1
  31. package/dist/components/material/SiteWrapper.js +22 -30
  32. package/dist/components/material/SiteWrapper.js.map +1 -1
  33. package/dist/helpers/SocketHelper.d.ts +3 -3
  34. package/dist/helpers/SocketHelper.d.ts.map +1 -1
  35. package/dist/helpers/SocketHelper.js +6 -6
  36. package/dist/helpers/SocketHelper.js.map +1 -1
  37. package/dist/interfaces/Messaging.d.ts +1 -2
  38. package/dist/interfaces/Messaging.d.ts.map +1 -1
  39. package/package.json +84 -84
  40. package/src/components/CreatePerson.tsx +80 -80
  41. package/src/components/DisplayBox.tsx +68 -68
  42. package/src/components/ErrorMessages.tsx +26 -26
  43. package/src/components/ExportLink.tsx +67 -67
  44. package/src/components/FloatingSupport.tsx +16 -16
  45. package/src/components/FormSubmissionEdit.tsx +120 -120
  46. package/src/components/HelpIcon.tsx +10 -10
  47. package/src/components/ImageEditor.tsx +126 -126
  48. package/src/components/InputBox.tsx +73 -73
  49. package/src/components/Loading.tsx +29 -29
  50. package/src/components/PersonAdd.tsx +75 -75
  51. package/src/components/QuestionEdit.tsx +62 -62
  52. package/src/components/SmallButton.tsx +39 -39
  53. package/src/components/SupportModal.tsx +26 -26
  54. package/src/components/TabPanel.tsx +34 -34
  55. package/src/components/gallery/GalleryModal.tsx +102 -102
  56. package/src/components/gallery/StockPhotos.tsx +74 -74
  57. package/src/components/gallery/index.ts +1 -1
  58. package/src/components/index.tsx +23 -23
  59. package/src/components/markdownEditor/Editor.tsx +132 -132
  60. package/src/components/markdownEditor/MarkdownEditor.tsx +16 -16
  61. package/src/components/markdownEditor/MarkdownModal.tsx +46 -46
  62. package/src/components/markdownEditor/MarkdownPreview.tsx +14 -14
  63. package/src/components/markdownEditor/editor.css +787 -787
  64. package/src/components/markdownEditor/images/icons/arrow-clockwise.svg +3 -3
  65. package/src/components/markdownEditor/images/icons/arrow-counterclockwise.svg +3 -3
  66. package/src/components/markdownEditor/images/icons/chat-square-quote.svg +3 -3
  67. package/src/components/markdownEditor/images/icons/chevron-down.svg +2 -2
  68. package/src/components/markdownEditor/images/icons/code.svg +2 -2
  69. package/src/components/markdownEditor/images/icons/journal-code.svg +4 -4
  70. package/src/components/markdownEditor/images/icons/journal-text.svg +4 -4
  71. package/src/components/markdownEditor/images/icons/justify.svg +2 -2
  72. package/src/components/markdownEditor/images/icons/link.svg +3 -3
  73. package/src/components/markdownEditor/images/icons/list-ol.svg +3 -3
  74. package/src/components/markdownEditor/images/icons/list-ul.svg +2 -2
  75. package/src/components/markdownEditor/images/icons/pencil-fill.svg +2 -2
  76. package/src/components/markdownEditor/images/icons/text-center.svg +2 -2
  77. package/src/components/markdownEditor/images/icons/text-left.svg +2 -2
  78. package/src/components/markdownEditor/images/icons/text-paragraph.svg +2 -2
  79. package/src/components/markdownEditor/images/icons/text-right.svg +2 -2
  80. package/src/components/markdownEditor/images/icons/type-bold.svg +2 -2
  81. package/src/components/markdownEditor/images/icons/type-h1.svg +2 -2
  82. package/src/components/markdownEditor/images/icons/type-h2.svg +2 -2
  83. package/src/components/markdownEditor/images/icons/type-h3.svg +2 -2
  84. package/src/components/markdownEditor/images/icons/type-h4.svg +12 -12
  85. package/src/components/markdownEditor/images/icons/type-italic.svg +2 -2
  86. package/src/components/markdownEditor/images/icons/type-strikethrough.svg +2 -2
  87. package/src/components/markdownEditor/images/icons/type-underline.svg +2 -2
  88. package/src/components/markdownEditor/index.ts +2 -2
  89. package/src/components/markdownEditor/plugins/AutoLinkPlugin.tsx +35 -35
  90. package/src/components/markdownEditor/plugins/ControlledEditorPlugin.tsx +24 -24
  91. package/src/components/markdownEditor/plugins/ListMaxIndentLevelPlugin.tsx +68 -68
  92. package/src/components/markdownEditor/plugins/MarkdownTransformers.ts +106 -106
  93. package/src/components/markdownEditor/plugins/ReadOnlyPlugin.tsx +15 -15
  94. package/src/components/markdownEditor/plugins/ToolbarPlugin.tsx +401 -401
  95. package/src/components/markdownEditor/plugins/customLink/CustomLinkNode.tsx +224 -224
  96. package/src/components/markdownEditor/plugins/customLink/CustomLinkNodePlugin.tsx +32 -32
  97. package/src/components/markdownEditor/plugins/customLink/CustomLinkNodeTransformer.tsx +102 -102
  98. package/src/components/markdownEditor/plugins/customLink/FloatingLinkEditor.tsx +243 -243
  99. package/src/components/markdownEditor/plugins/customLink/FloatingLinkEditor.types.ts +11 -11
  100. package/src/components/markdownEditor/plugins/emoji/EmojiNode.tsx +95 -95
  101. package/src/components/markdownEditor/plugins/emoji/EmojiNodeTransform.ts +41 -41
  102. package/src/components/markdownEditor/plugins/emoji/EmojiPickerPlugin.tsx +152 -152
  103. package/src/components/markdownEditor/plugins/emoji/EmojisPlugin.tsx +65 -65
  104. package/src/components/markdownEditor/plugins/index.ts +6 -6
  105. package/src/components/markdownEditor/theme.ts +65 -65
  106. package/src/components/material/AppList.tsx +20 -20
  107. package/src/components/material/ChurchList.tsx +22 -22
  108. package/src/components/material/NavItem.tsx +41 -41
  109. package/src/components/material/NewPrivateMessage.tsx +103 -103
  110. package/src/components/material/PrivateMessageDetails.tsx +23 -23
  111. package/src/components/material/PrivateMessages.tsx +87 -87
  112. package/src/components/material/SiteWrapper.tsx +143 -140
  113. package/src/components/material/UserMenu.tsx +141 -141
  114. package/src/components/material/iconPicker/IconNamesList.ts +2240 -2240
  115. package/src/components/material/iconPicker/IconPicker.tsx +153 -153
  116. package/src/components/material/index.tsx +6 -6
  117. package/src/components/notes/AddNote.tsx +90 -90
  118. package/src/components/notes/Conversation.tsx +82 -82
  119. package/src/components/notes/Conversations.tsx +58 -58
  120. package/src/components/notes/NewConversation.tsx +78 -78
  121. package/src/components/notes/Note.tsx +44 -44
  122. package/src/components/notes/Notes.tsx +52 -52
  123. package/src/components/notes/index.ts +5 -5
  124. package/src/components/reporting/ChartReport.tsx +98 -98
  125. package/src/components/reporting/ReportFilter.tsx +54 -54
  126. package/src/components/reporting/ReportFilterField.tsx +160 -160
  127. package/src/components/reporting/ReportOutput.tsx +79 -79
  128. package/src/components/reporting/ReportWithFilter.tsx +70 -70
  129. package/src/components/reporting/TableReport.tsx +57 -57
  130. package/src/components/reporting/TreeReport.tsx +111 -111
  131. package/src/components/reporting/index.ts +4 -4
  132. package/src/donationComponents/DonationPage.tsx +136 -136
  133. package/src/donationComponents/components/BankForm.tsx +159 -159
  134. package/src/donationComponents/components/CardForm.tsx +104 -104
  135. package/src/donationComponents/components/DonationForm.tsx +235 -235
  136. package/src/donationComponents/components/FundDonation.tsx +49 -49
  137. package/src/donationComponents/components/FundDonations.tsx +39 -39
  138. package/src/donationComponents/components/NonAuthDonation.tsx +31 -31
  139. package/src/donationComponents/components/NonAuthDonationInner.tsx +259 -259
  140. package/src/donationComponents/components/PaymentMethods.tsx +135 -135
  141. package/src/donationComponents/components/RecurringDonations.tsx +121 -121
  142. package/src/donationComponents/components/RecurringDonationsEdit.tsx +93 -93
  143. package/src/donationComponents/components/index.tsx +9 -9
  144. package/src/donationComponents/index.ts +3 -3
  145. package/src/donationComponents/modals/DonationPreviewModal.tsx +66 -66
  146. package/src/helpers/AnalyticsHelper.ts +33 -33
  147. package/src/helpers/ApiHelper.ts +125 -125
  148. package/src/helpers/AppearanceHelper.ts +69 -69
  149. package/src/helpers/ArrayHelper.ts +81 -81
  150. package/src/helpers/CommonEnvironmentHelper.ts +80 -80
  151. package/src/helpers/CurrencyHelper.ts +10 -10
  152. package/src/helpers/DateHelper.ts +108 -108
  153. package/src/helpers/DonationHelper.ts +26 -26
  154. package/src/helpers/ErrorHelper.ts +36 -36
  155. package/src/helpers/EventHelper.ts +52 -52
  156. package/src/helpers/FileHelper.ts +31 -31
  157. package/src/helpers/PersonHelper.ts +60 -60
  158. package/src/helpers/SocketHelper.ts +76 -76
  159. package/src/helpers/Themes.ts +14 -14
  160. package/src/helpers/UniqueIdHelper.ts +36 -36
  161. package/src/helpers/UserHelper.ts +59 -59
  162. package/src/helpers/createEmotionCache.ts +17 -17
  163. package/src/helpers/index.ts +18 -18
  164. package/src/hooks/index.ts +1 -1
  165. package/src/hooks/useMountedState.ts +16 -16
  166. package/src/index.ts +6 -6
  167. package/src/interfaces/Access.ts +24 -24
  168. package/src/interfaces/Attendance.ts +8 -8
  169. package/src/interfaces/Content.ts +10 -10
  170. package/src/interfaces/Doing.ts +24 -24
  171. package/src/interfaces/Donation.ts +45 -45
  172. package/src/interfaces/Error.ts +17 -17
  173. package/src/interfaces/Membership.ts +51 -51
  174. package/src/interfaces/Messaging.ts +20 -20
  175. package/src/interfaces/Permissions.ts +68 -68
  176. package/src/interfaces/Reporting.ts +7 -7
  177. package/src/interfaces/UserContextInterface.ts +13 -13
  178. package/src/interfaces/index.ts +13 -13
  179. package/src/pageComponents/LoginPage.tsx +244 -244
  180. package/src/pageComponents/LogoutPage.tsx +28 -28
  181. package/src/pageComponents/components/Forgot.tsx +79 -79
  182. package/src/pageComponents/components/Login.tsx +54 -54
  183. package/src/pageComponents/components/LoginSetPassword.tsx +63 -63
  184. package/src/pageComponents/components/Register.tsx +107 -107
  185. package/src/pageComponents/components/SelectChurchModal.tsx +41 -41
  186. package/src/pageComponents/components/SelectChurchRegister.tsx +88 -88
  187. package/src/pageComponents/components/SelectChurchSearch.tsx +69 -69
  188. package/src/pageComponents/components/SelectableChurch.tsx +38 -38
  189. package/src/pageComponents/index.ts +3 -3
  190. package/tsconfig.json +34 -34
  191. package/tslint.json +14 -14
@@ -1,235 +1,235 @@
1
- /* eslint-disable multiline-ternary */
2
- import React from "react";
3
- import { Stripe } from "@stripe/stripe-js";
4
- import { InputBox, ErrorMessages } from "../../components";
5
- import { FundDonations } from ".";
6
- import { DonationPreviewModal } from "../modals/DonationPreviewModal";
7
- import { ApiHelper, CurrencyHelper, DateHelper } from "../../helpers";
8
- import { PersonInterface, StripePaymentMethod, StripeDonationInterface, FundDonationInterface, FundInterface, ChurchInterface } from "../../interfaces";
9
- import { Grid, InputLabel, MenuItem, Select, TextField, FormControl, Button, SelectChangeEvent, FormControlLabel, Checkbox, FormGroup, Typography } from "@mui/material"
10
- import { DonationHelper } from "../../helpers";
11
-
12
- interface Props { person: PersonInterface, customerId: string, paymentMethods: StripePaymentMethod[], stripePromise: Promise<Stripe>, donationSuccess: (message: string) => void, church?: ChurchInterface, churchLogo?: string }
13
-
14
- export const DonationForm: React.FC<Props> = (props) => {
15
- const [errorMessage, setErrorMessage] = React.useState<string>();
16
- const [fundDonations, setFundDonations] = React.useState<FundDonationInterface[]>();
17
- const [funds, setFunds] = React.useState<FundInterface[]>([]);
18
- const [fundsTotal, setFundsTotal] = React.useState<number>(0);
19
- const [transactionFee, setTransactionFee] = React.useState<number>(0);
20
- const [payFee, setPayFee] = React.useState<number>(0);
21
- const [total, setTotal] = React.useState<number>(0);
22
- const [paymentMethodName, setPaymentMethodName] = React.useState<string>(`${props?.paymentMethods[0]?.name} ****${props?.paymentMethods[0]?.last4}`);
23
- const [donationType, setDonationType] = React.useState<string>();
24
- const [showDonationPreviewModal, setShowDonationPreviewModal] = React.useState<boolean>(false);
25
- const [interval, setInterval] = React.useState("one_month");
26
- const [gateway, setGateway] = React.useState(null);
27
- const [donation, setDonation] = React.useState<StripeDonationInterface>({
28
- id: props?.paymentMethods[0]?.id,
29
- type: props?.paymentMethods[0]?.type,
30
- customerId: props.customerId,
31
- person: {
32
- id: props.person?.id,
33
- email: props.person?.contactInfo.email,
34
- name: props.person?.name.display
35
- },
36
- amount: 0,
37
- billing_cycle_anchor: + new Date(),
38
- interval: {
39
- interval_count: 1,
40
- interval: "month"
41
- },
42
- funds: []
43
- });
44
-
45
- const loadData = () => {
46
- ApiHelper.get("/funds", "GivingApi").then(data => {
47
- setFunds(data);
48
- if (data.length) setFundDonations([{ fundId: data[0].id }]);
49
- });
50
- ApiHelper.get("/gateways", "GivingApi").then((data) => {
51
- if (data.length !== 0) setGateway(data[0]);
52
- });
53
- }
54
-
55
- const handleKeyDown = (e: React.KeyboardEvent<any>) => { if (e.key === "Enter") { e.preventDefault(); handleSave(); } }
56
-
57
- const handleCheckChange = (e: React.SyntheticEvent<Element, Event>, checked: boolean) => {
58
- let d = { ...donation } as StripeDonationInterface;
59
- d.amount = checked ? fundsTotal + transactionFee : fundsTotal;
60
- let showFee = checked ? transactionFee : 0;
61
- setTotal(d.amount);
62
- setPayFee(showFee);
63
- setDonation(d);
64
- }
65
-
66
- const handleAutoPayFee = () => {
67
- let d = { ...donation } as StripeDonationInterface;
68
- d.amount = fundsTotal + transactionFee;
69
- let showFee = transactionFee;
70
- setTotal(d.amount);
71
- setPayFee(showFee);
72
- setDonation(d);
73
- }
74
-
75
- const handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement> | SelectChangeEvent<string>) => {
76
- setErrorMessage(null);
77
- let d = { ...donation } as StripeDonationInterface;
78
- let value = e.target.value;
79
- switch (e.target.name) {
80
- case "method":
81
- d.id = value;
82
- let pm = props.paymentMethods.find(pm => pm.id === value);
83
- d.type = pm.type;
84
- setPaymentMethodName(`${pm.name} ****${pm.last4}`);
85
- break;
86
- case "type": setDonationType(value); break;
87
- case "date": d.billing_cycle_anchor = + new Date(value); break;
88
- case "interval":
89
- setInterval(value);
90
- d.interval = DonationHelper.getInterval(value);
91
- break;
92
- case "notes": d.notes = value; break;
93
- case "transaction-fee":
94
- const element = e.target as HTMLInputElement
95
- d.amount = element.checked ? fundsTotal + transactionFee : fundsTotal;
96
- let showFee = element.checked ? transactionFee : 0;
97
- setTotal(d.amount);
98
- setPayFee(showFee);
99
- }
100
- setDonation(d);
101
- }
102
-
103
- const handleCancel = () => { setDonationType(null); }
104
- const handleSave = () => {
105
- if (donation.amount < .5) setErrorMessage("Donation amount must be greater than $0.50");
106
- else setShowDonationPreviewModal(true);
107
- }
108
- const handleDonationSelect = (type: string) => {
109
- let dt = donationType === type ? null : type;
110
- setDonationType(dt);
111
- }
112
-
113
- const makeDonation = async (message: string) => {
114
- let results;
115
-
116
- const churchObj = {
117
- name: props?.church?.name,
118
- subDomain: props?.church?.subDomain,
119
- churchURL: typeof window !== "undefined" && window.location.origin,
120
- logo: props?.churchLogo
121
- }
122
-
123
- if (donationType === "once") results = await ApiHelper.post("/donate/charge/", {...donation, church: churchObj }, "GivingApi");
124
- if (donationType === "recurring") results = await ApiHelper.post("/donate/subscribe/", {...donation, church: churchObj }, "GivingApi");
125
-
126
- if (results?.status === "succeeded" || results?.status === "pending" || results?.status === "active") {
127
- setShowDonationPreviewModal(false);
128
- setDonationType(null);
129
- props.donationSuccess(message);
130
- }
131
- if (results?.raw?.message) {
132
- setShowDonationPreviewModal(false);
133
- setErrorMessage("Error: " + results?.raw?.message);
134
- }
135
- }
136
-
137
- const handleFundDonationsChange = (fd: FundDonationInterface[]) => {
138
- setErrorMessage(null);
139
- setFundDonations(fd);
140
- let totalAmount = 0;
141
- let selectedFunds: any = [];
142
- for (const fundDonation of fd) {
143
- totalAmount += fundDonation.amount || 0;
144
- let fund = funds.find((fund: FundInterface) => fund.id === fundDonation.fundId);
145
- selectedFunds.push({ id: fundDonation.fundId, amount: fundDonation.amount || 0, name: fund.name });
146
- }
147
- let d = { ...donation };
148
- d.amount = totalAmount;
149
- d.funds = selectedFunds;
150
- setDonation(d);
151
- setFundsTotal(totalAmount);
152
- setTotal(totalAmount);
153
- setTransactionFee(getTransactionFee(totalAmount));
154
- }
155
-
156
- const getTransactionFee = (amount: number) => {
157
- const fixedFee = 0.30;
158
- const fixedPercent = 0.029;
159
- return Math.round(((amount + fixedFee) / (1 - fixedPercent) - amount) * 100) / 100;
160
- }
161
-
162
- React.useEffect(loadData, [props.person?.id]);
163
- // eslint-disable-next-line react-hooks/exhaustive-deps
164
- React.useEffect(() => { gateway && gateway.payFees === true && handleAutoPayFee() }, [fundDonations]);
165
-
166
- if (!funds.length || !props?.paymentMethods[0]?.id) return null;
167
- else return (
168
- <>
169
- <DonationPreviewModal show={showDonationPreviewModal} onHide={() => setShowDonationPreviewModal(false)} handleDonate={makeDonation} donation={donation} donationType={donationType} payFee={payFee} paymentMethodName={paymentMethodName} funds={funds} />
170
- <InputBox id="donationBox" aria-label="donation-box" headerIcon="volunteer_activism" headerText="Donate" ariaLabelSave="save-button" cancelFunction={donationType ? handleCancel : undefined} saveFunction={donationType ? handleSave : undefined} saveText="Preview Donation">
171
- <Grid container spacing={3}>
172
- <Grid item md={6} xs={12}>
173
- <Button aria-label="single-donation" size="small" fullWidth style={{ minHeight: "50px" }} variant={donationType === "once" ? "contained" : "outlined"} onClick={() => handleDonationSelect("once")}>Make a Donation</Button>
174
- </Grid>
175
- <Grid item md={6} xs={12}>
176
- <Button aria-label="recurring-donation" size="small" fullWidth style={{ minHeight: "50px" }} variant={donationType === "recurring" ? "contained" : "outlined"} onClick={() => handleDonationSelect("recurring")}>Make a Recurring Donation</Button>
177
- </Grid>
178
- </Grid>
179
- {donationType
180
- && <div style={{ marginTop: "20px" }}>
181
- <Grid container spacing={3}>
182
- <Grid item md={12} xs={12}>
183
- <FormControl fullWidth>
184
- <InputLabel>Method</InputLabel>
185
- <Select label="Method" name="method" aria-label="method" value={donation.id} className="capitalize" onChange={handleChange}>
186
- {props.paymentMethods.map((paymentMethod: any, i: number) => <MenuItem key={i} value={paymentMethod.id}>{paymentMethod.name} ****{paymentMethod.last4}</MenuItem>)}
187
- </Select>
188
- </FormControl>
189
- </Grid>
190
- </Grid>
191
- {donationType === "recurring"
192
- && <Grid container spacing={3} style={{marginTop:10}}>
193
- <Grid item md={6} xs={12}>
194
- <TextField fullWidth name="date" type="date" aria-label="date" label="Start Date" value={DateHelper.formatHtml5Date(new Date(donation.billing_cycle_anchor))} onChange={handleChange} onKeyDown={handleKeyDown} />
195
- </Grid>
196
- <Grid item md={6} xs={12}>
197
- <FormControl fullWidth>
198
- <InputLabel>Frequency</InputLabel>
199
- <Select label="Frequency" name="interval" aria-label="interval" value={interval} onChange={handleChange}>
200
- <MenuItem value="one_week">Weekly</MenuItem>
201
- <MenuItem value="two_week">Bi-Weekly</MenuItem>
202
- <MenuItem value="one_month">Monthly</MenuItem>
203
- <MenuItem value="three_month">Quarterly</MenuItem>
204
- <MenuItem value="one_year">Annually</MenuItem>
205
- </Select>
206
- </FormControl>
207
- </Grid>
208
- </Grid>
209
- }
210
- <div className="form-group">
211
- {funds && fundDonations
212
- && <>
213
- <h4>Fund</h4>
214
- <FundDonations fundDonations={fundDonations} funds={funds} updatedFunction={handleFundDonationsChange} />
215
- </>
216
- }
217
- {fundsTotal > 0
218
- && <>
219
- {(gateway && gateway.payFees === true) ? <Typography fontSize={14} fontStyle="italic">*Transaction fees of {CurrencyHelper.formatCurrency(transactionFee)} are applied.</Typography> : (
220
- <FormGroup>
221
- <FormControlLabel control={<Checkbox />} name="transaction-fee" label={`I'll generously add ${CurrencyHelper.formatCurrency(transactionFee)} to cover the transaction fees so you can keep 100% of my donation.`} onChange={handleCheckChange} />
222
- </FormGroup>
223
- )}
224
- <p>Total Donation Amount: ${total}</p>
225
- </>
226
- }
227
- <TextField fullWidth label="Notes" multiline aria-label="note" name="notes" value={donation.notes || ""} onChange={handleChange} onKeyDown={handleKeyDown} />
228
- </div>
229
- {errorMessage && <ErrorMessages errors={[errorMessage]}></ErrorMessages>}
230
- </div>
231
- }
232
- </InputBox>
233
- </>
234
- );
235
- }
1
+ /* eslint-disable multiline-ternary */
2
+ import React from "react";
3
+ import { Stripe } from "@stripe/stripe-js";
4
+ import { InputBox, ErrorMessages } from "../../components";
5
+ import { FundDonations } from ".";
6
+ import { DonationPreviewModal } from "../modals/DonationPreviewModal";
7
+ import { ApiHelper, CurrencyHelper, DateHelper } from "../../helpers";
8
+ import { PersonInterface, StripePaymentMethod, StripeDonationInterface, FundDonationInterface, FundInterface, ChurchInterface } from "../../interfaces";
9
+ import { Grid, InputLabel, MenuItem, Select, TextField, FormControl, Button, SelectChangeEvent, FormControlLabel, Checkbox, FormGroup, Typography } from "@mui/material"
10
+ import { DonationHelper } from "../../helpers";
11
+
12
+ interface Props { person: PersonInterface, customerId: string, paymentMethods: StripePaymentMethod[], stripePromise: Promise<Stripe>, donationSuccess: (message: string) => void, church?: ChurchInterface, churchLogo?: string }
13
+
14
+ export const DonationForm: React.FC<Props> = (props) => {
15
+ const [errorMessage, setErrorMessage] = React.useState<string>();
16
+ const [fundDonations, setFundDonations] = React.useState<FundDonationInterface[]>();
17
+ const [funds, setFunds] = React.useState<FundInterface[]>([]);
18
+ const [fundsTotal, setFundsTotal] = React.useState<number>(0);
19
+ const [transactionFee, setTransactionFee] = React.useState<number>(0);
20
+ const [payFee, setPayFee] = React.useState<number>(0);
21
+ const [total, setTotal] = React.useState<number>(0);
22
+ const [paymentMethodName, setPaymentMethodName] = React.useState<string>(`${props?.paymentMethods[0]?.name} ****${props?.paymentMethods[0]?.last4}`);
23
+ const [donationType, setDonationType] = React.useState<string>();
24
+ const [showDonationPreviewModal, setShowDonationPreviewModal] = React.useState<boolean>(false);
25
+ const [interval, setInterval] = React.useState("one_month");
26
+ const [gateway, setGateway] = React.useState(null);
27
+ const [donation, setDonation] = React.useState<StripeDonationInterface>({
28
+ id: props?.paymentMethods[0]?.id,
29
+ type: props?.paymentMethods[0]?.type,
30
+ customerId: props.customerId,
31
+ person: {
32
+ id: props.person?.id,
33
+ email: props.person?.contactInfo.email,
34
+ name: props.person?.name.display
35
+ },
36
+ amount: 0,
37
+ billing_cycle_anchor: + new Date(),
38
+ interval: {
39
+ interval_count: 1,
40
+ interval: "month"
41
+ },
42
+ funds: []
43
+ });
44
+
45
+ const loadData = () => {
46
+ ApiHelper.get("/funds", "GivingApi").then(data => {
47
+ setFunds(data);
48
+ if (data.length) setFundDonations([{ fundId: data[0].id }]);
49
+ });
50
+ ApiHelper.get("/gateways", "GivingApi").then((data) => {
51
+ if (data.length !== 0) setGateway(data[0]);
52
+ });
53
+ }
54
+
55
+ const handleKeyDown = (e: React.KeyboardEvent<any>) => { if (e.key === "Enter") { e.preventDefault(); handleSave(); } }
56
+
57
+ const handleCheckChange = (e: React.SyntheticEvent<Element, Event>, checked: boolean) => {
58
+ let d = { ...donation } as StripeDonationInterface;
59
+ d.amount = checked ? fundsTotal + transactionFee : fundsTotal;
60
+ let showFee = checked ? transactionFee : 0;
61
+ setTotal(d.amount);
62
+ setPayFee(showFee);
63
+ setDonation(d);
64
+ }
65
+
66
+ const handleAutoPayFee = () => {
67
+ let d = { ...donation } as StripeDonationInterface;
68
+ d.amount = fundsTotal + transactionFee;
69
+ let showFee = transactionFee;
70
+ setTotal(d.amount);
71
+ setPayFee(showFee);
72
+ setDonation(d);
73
+ }
74
+
75
+ const handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement> | SelectChangeEvent<string>) => {
76
+ setErrorMessage(null);
77
+ let d = { ...donation } as StripeDonationInterface;
78
+ let value = e.target.value;
79
+ switch (e.target.name) {
80
+ case "method":
81
+ d.id = value;
82
+ let pm = props.paymentMethods.find(pm => pm.id === value);
83
+ d.type = pm.type;
84
+ setPaymentMethodName(`${pm.name} ****${pm.last4}`);
85
+ break;
86
+ case "type": setDonationType(value); break;
87
+ case "date": d.billing_cycle_anchor = + new Date(value); break;
88
+ case "interval":
89
+ setInterval(value);
90
+ d.interval = DonationHelper.getInterval(value);
91
+ break;
92
+ case "notes": d.notes = value; break;
93
+ case "transaction-fee":
94
+ const element = e.target as HTMLInputElement
95
+ d.amount = element.checked ? fundsTotal + transactionFee : fundsTotal;
96
+ let showFee = element.checked ? transactionFee : 0;
97
+ setTotal(d.amount);
98
+ setPayFee(showFee);
99
+ }
100
+ setDonation(d);
101
+ }
102
+
103
+ const handleCancel = () => { setDonationType(null); }
104
+ const handleSave = () => {
105
+ if (donation.amount < .5) setErrorMessage("Donation amount must be greater than $0.50");
106
+ else setShowDonationPreviewModal(true);
107
+ }
108
+ const handleDonationSelect = (type: string) => {
109
+ let dt = donationType === type ? null : type;
110
+ setDonationType(dt);
111
+ }
112
+
113
+ const makeDonation = async (message: string) => {
114
+ let results;
115
+
116
+ const churchObj = {
117
+ name: props?.church?.name,
118
+ subDomain: props?.church?.subDomain,
119
+ churchURL: typeof window !== "undefined" && window.location.origin,
120
+ logo: props?.churchLogo
121
+ }
122
+
123
+ if (donationType === "once") results = await ApiHelper.post("/donate/charge/", {...donation, church: churchObj }, "GivingApi");
124
+ if (donationType === "recurring") results = await ApiHelper.post("/donate/subscribe/", {...donation, church: churchObj }, "GivingApi");
125
+
126
+ if (results?.status === "succeeded" || results?.status === "pending" || results?.status === "active") {
127
+ setShowDonationPreviewModal(false);
128
+ setDonationType(null);
129
+ props.donationSuccess(message);
130
+ }
131
+ if (results?.raw?.message) {
132
+ setShowDonationPreviewModal(false);
133
+ setErrorMessage("Error: " + results?.raw?.message);
134
+ }
135
+ }
136
+
137
+ const handleFundDonationsChange = (fd: FundDonationInterface[]) => {
138
+ setErrorMessage(null);
139
+ setFundDonations(fd);
140
+ let totalAmount = 0;
141
+ let selectedFunds: any = [];
142
+ for (const fundDonation of fd) {
143
+ totalAmount += fundDonation.amount || 0;
144
+ let fund = funds.find((fund: FundInterface) => fund.id === fundDonation.fundId);
145
+ selectedFunds.push({ id: fundDonation.fundId, amount: fundDonation.amount || 0, name: fund.name });
146
+ }
147
+ let d = { ...donation };
148
+ d.amount = totalAmount;
149
+ d.funds = selectedFunds;
150
+ setDonation(d);
151
+ setFundsTotal(totalAmount);
152
+ setTotal(totalAmount);
153
+ setTransactionFee(getTransactionFee(totalAmount));
154
+ }
155
+
156
+ const getTransactionFee = (amount: number) => {
157
+ const fixedFee = 0.30;
158
+ const fixedPercent = 0.029;
159
+ return Math.round(((amount + fixedFee) / (1 - fixedPercent) - amount) * 100) / 100;
160
+ }
161
+
162
+ React.useEffect(loadData, [props.person?.id]);
163
+ // eslint-disable-next-line react-hooks/exhaustive-deps
164
+ React.useEffect(() => { gateway && gateway.payFees === true && handleAutoPayFee() }, [fundDonations]);
165
+
166
+ if (!funds.length || !props?.paymentMethods[0]?.id) return null;
167
+ else return (
168
+ <>
169
+ <DonationPreviewModal show={showDonationPreviewModal} onHide={() => setShowDonationPreviewModal(false)} handleDonate={makeDonation} donation={donation} donationType={donationType} payFee={payFee} paymentMethodName={paymentMethodName} funds={funds} />
170
+ <InputBox id="donationBox" aria-label="donation-box" headerIcon="volunteer_activism" headerText="Donate" ariaLabelSave="save-button" cancelFunction={donationType ? handleCancel : undefined} saveFunction={donationType ? handleSave : undefined} saveText="Preview Donation">
171
+ <Grid container spacing={3}>
172
+ <Grid item md={6} xs={12}>
173
+ <Button aria-label="single-donation" size="small" fullWidth style={{ minHeight: "50px" }} variant={donationType === "once" ? "contained" : "outlined"} onClick={() => handleDonationSelect("once")}>Make a Donation</Button>
174
+ </Grid>
175
+ <Grid item md={6} xs={12}>
176
+ <Button aria-label="recurring-donation" size="small" fullWidth style={{ minHeight: "50px" }} variant={donationType === "recurring" ? "contained" : "outlined"} onClick={() => handleDonationSelect("recurring")}>Make a Recurring Donation</Button>
177
+ </Grid>
178
+ </Grid>
179
+ {donationType
180
+ && <div style={{ marginTop: "20px" }}>
181
+ <Grid container spacing={3}>
182
+ <Grid item md={12} xs={12}>
183
+ <FormControl fullWidth>
184
+ <InputLabel>Method</InputLabel>
185
+ <Select label="Method" name="method" aria-label="method" value={donation.id} className="capitalize" onChange={handleChange}>
186
+ {props.paymentMethods.map((paymentMethod: any, i: number) => <MenuItem key={i} value={paymentMethod.id}>{paymentMethod.name} ****{paymentMethod.last4}</MenuItem>)}
187
+ </Select>
188
+ </FormControl>
189
+ </Grid>
190
+ </Grid>
191
+ {donationType === "recurring"
192
+ && <Grid container spacing={3} style={{marginTop:10}}>
193
+ <Grid item md={6} xs={12}>
194
+ <TextField fullWidth name="date" type="date" aria-label="date" label="Start Date" value={DateHelper.formatHtml5Date(new Date(donation.billing_cycle_anchor))} onChange={handleChange} onKeyDown={handleKeyDown} />
195
+ </Grid>
196
+ <Grid item md={6} xs={12}>
197
+ <FormControl fullWidth>
198
+ <InputLabel>Frequency</InputLabel>
199
+ <Select label="Frequency" name="interval" aria-label="interval" value={interval} onChange={handleChange}>
200
+ <MenuItem value="one_week">Weekly</MenuItem>
201
+ <MenuItem value="two_week">Bi-Weekly</MenuItem>
202
+ <MenuItem value="one_month">Monthly</MenuItem>
203
+ <MenuItem value="three_month">Quarterly</MenuItem>
204
+ <MenuItem value="one_year">Annually</MenuItem>
205
+ </Select>
206
+ </FormControl>
207
+ </Grid>
208
+ </Grid>
209
+ }
210
+ <div className="form-group">
211
+ {funds && fundDonations
212
+ && <>
213
+ <h4>Fund</h4>
214
+ <FundDonations fundDonations={fundDonations} funds={funds} updatedFunction={handleFundDonationsChange} />
215
+ </>
216
+ }
217
+ {fundsTotal > 0
218
+ && <>
219
+ {(gateway && gateway.payFees === true) ? <Typography fontSize={14} fontStyle="italic">*Transaction fees of {CurrencyHelper.formatCurrency(transactionFee)} are applied.</Typography> : (
220
+ <FormGroup>
221
+ <FormControlLabel control={<Checkbox />} name="transaction-fee" label={`I'll generously add ${CurrencyHelper.formatCurrency(transactionFee)} to cover the transaction fees so you can keep 100% of my donation.`} onChange={handleCheckChange} />
222
+ </FormGroup>
223
+ )}
224
+ <p>Total Donation Amount: ${total}</p>
225
+ </>
226
+ }
227
+ <TextField fullWidth label="Notes" multiline aria-label="note" name="notes" value={donation.notes || ""} onChange={handleChange} onKeyDown={handleKeyDown} />
228
+ </div>
229
+ {errorMessage && <ErrorMessages errors={[errorMessage]}></ErrorMessages>}
230
+ </div>
231
+ }
232
+ </InputBox>
233
+ </>
234
+ );
235
+ }
@@ -1,49 +1,49 @@
1
- import React from "react";
2
- import { FundDonationInterface, FundInterface } from "../../interfaces";
3
- import { FormControl, Grid, InputLabel, MenuItem, Select, SelectChangeEvent, TextField } from "@mui/material"
4
-
5
- interface Props {
6
- fundDonation: FundDonationInterface,
7
- funds: FundInterface[],
8
- index: number,
9
- updatedFunction: (fundDonation: FundDonationInterface, index: number) => void
10
- }
11
-
12
- export const FundDonation: React.FC<Props> = (props) => {
13
-
14
- const getOptions = () => {
15
- let result = [];
16
- for (let i = 0; i < props.funds.length; i++) result.push(<MenuItem key={i} value={props.funds[i].id}>{props.funds[i].name}</MenuItem>);
17
- return result;
18
- }
19
-
20
- const handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement> | SelectChangeEvent<string>) => {
21
- let fd = { ...props.fundDonation }
22
- switch (e.target.name) {
23
- case "amount":
24
- fd.amount = parseFloat(e.target.value.replace("$", "").replace(",", ""));
25
- break;
26
- case "fund":
27
- fd.fundId = e.target.value;
28
- break;
29
- }
30
- props.updatedFunction(fd, props.index);
31
- }
32
-
33
- return (
34
- <Grid container spacing={3}>
35
- <Grid item md={6} xs={12}>
36
- <TextField fullWidth name="amount" label="Amount" type="number" aria-label="amount" lang="en-150" value={props.fundDonation.amount || ""} onChange={handleChange} />
37
- </Grid>
38
- <Grid item md={6} xs={12}>
39
- <FormControl fullWidth>
40
- <InputLabel>Fund</InputLabel>
41
- <Select fullWidth label="Fund" name="fund" aria-label="fund" value={props.fundDonation.fundId} onChange={handleChange}>
42
- {getOptions()}
43
- </Select>
44
- </FormControl>
45
- </Grid>
46
- </Grid>
47
- );
48
- }
49
-
1
+ import React from "react";
2
+ import { FundDonationInterface, FundInterface } from "../../interfaces";
3
+ import { FormControl, Grid, InputLabel, MenuItem, Select, SelectChangeEvent, TextField } from "@mui/material"
4
+
5
+ interface Props {
6
+ fundDonation: FundDonationInterface,
7
+ funds: FundInterface[],
8
+ index: number,
9
+ updatedFunction: (fundDonation: FundDonationInterface, index: number) => void
10
+ }
11
+
12
+ export const FundDonation: React.FC<Props> = (props) => {
13
+
14
+ const getOptions = () => {
15
+ let result = [];
16
+ for (let i = 0; i < props.funds.length; i++) result.push(<MenuItem key={i} value={props.funds[i].id}>{props.funds[i].name}</MenuItem>);
17
+ return result;
18
+ }
19
+
20
+ const handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement> | SelectChangeEvent<string>) => {
21
+ let fd = { ...props.fundDonation }
22
+ switch (e.target.name) {
23
+ case "amount":
24
+ fd.amount = parseFloat(e.target.value.replace("$", "").replace(",", ""));
25
+ break;
26
+ case "fund":
27
+ fd.fundId = e.target.value;
28
+ break;
29
+ }
30
+ props.updatedFunction(fd, props.index);
31
+ }
32
+
33
+ return (
34
+ <Grid container spacing={3}>
35
+ <Grid item md={6} xs={12}>
36
+ <TextField fullWidth name="amount" label="Amount" type="number" aria-label="amount" lang="en-150" value={props.fundDonation.amount || ""} onChange={handleChange} />
37
+ </Grid>
38
+ <Grid item md={6} xs={12}>
39
+ <FormControl fullWidth>
40
+ <InputLabel>Fund</InputLabel>
41
+ <Select fullWidth label="Fund" name="fund" aria-label="fund" value={props.fundDonation.fundId} onChange={handleChange}>
42
+ {getOptions()}
43
+ </Select>
44
+ </FormControl>
45
+ </Grid>
46
+ </Grid>
47
+ );
48
+ }
49
+