@churchapps/apphelper 0.1.0 → 0.1.1

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 (194) hide show
  1. package/.eslintignore +3 -3
  2. package/.eslintrc.json +22 -22
  3. package/LICENSE +21 -21
  4. package/README.md +22 -22
  5. package/dist/components/FormSubmissionEdit.d.ts.map +1 -1
  6. package/dist/components/FormSubmissionEdit.js +9 -2
  7. package/dist/components/FormSubmissionEdit.js.map +1 -1
  8. package/dist/components/QuestionEdit.d.ts.map +1 -1
  9. package/dist/components/QuestionEdit.js +2 -1
  10. package/dist/components/QuestionEdit.js.map +1 -1
  11. package/dist/components/markdownEditor/editor.css +787 -787
  12. package/dist/components/markdownEditor/images/icons/arrow-clockwise.svg +3 -3
  13. package/dist/components/markdownEditor/images/icons/arrow-counterclockwise.svg +3 -3
  14. package/dist/components/markdownEditor/images/icons/chat-square-quote.svg +3 -3
  15. package/dist/components/markdownEditor/images/icons/chevron-down.svg +2 -2
  16. package/dist/components/markdownEditor/images/icons/code.svg +2 -2
  17. package/dist/components/markdownEditor/images/icons/journal-code.svg +4 -4
  18. package/dist/components/markdownEditor/images/icons/journal-text.svg +4 -4
  19. package/dist/components/markdownEditor/images/icons/justify.svg +2 -2
  20. package/dist/components/markdownEditor/images/icons/link.svg +3 -3
  21. package/dist/components/markdownEditor/images/icons/list-ol.svg +3 -3
  22. package/dist/components/markdownEditor/images/icons/list-ul.svg +2 -2
  23. package/dist/components/markdownEditor/images/icons/pencil-fill.svg +2 -2
  24. package/dist/components/markdownEditor/images/icons/text-center.svg +2 -2
  25. package/dist/components/markdownEditor/images/icons/text-left.svg +2 -2
  26. package/dist/components/markdownEditor/images/icons/text-paragraph.svg +2 -2
  27. package/dist/components/markdownEditor/images/icons/text-right.svg +2 -2
  28. package/dist/components/markdownEditor/images/icons/type-bold.svg +2 -2
  29. package/dist/components/markdownEditor/images/icons/type-h1.svg +2 -2
  30. package/dist/components/markdownEditor/images/icons/type-h2.svg +2 -2
  31. package/dist/components/markdownEditor/images/icons/type-h3.svg +2 -2
  32. package/dist/components/markdownEditor/images/icons/type-h4.svg +12 -12
  33. package/dist/components/markdownEditor/images/icons/type-italic.svg +2 -2
  34. package/dist/components/markdownEditor/images/icons/type-strikethrough.svg +2 -2
  35. package/dist/components/markdownEditor/images/icons/type-underline.svg +2 -2
  36. package/dist/components/wrapper/SiteWrapper.d.ts.map +1 -1
  37. package/dist/components/wrapper/SiteWrapper.js.map +1 -1
  38. package/package.json +85 -84
  39. package/src/components/CreatePerson.tsx +80 -80
  40. package/src/components/DisplayBox.tsx +68 -68
  41. package/src/components/ErrorMessages.tsx +26 -26
  42. package/src/components/ExportLink.tsx +67 -67
  43. package/src/components/FloatingSupport.tsx +16 -16
  44. package/src/components/FormSubmissionEdit.tsx +129 -120
  45. package/src/components/HelpIcon.tsx +10 -10
  46. package/src/components/ImageEditor.tsx +126 -126
  47. package/src/components/InputBox.tsx +73 -73
  48. package/src/components/Loading.tsx +29 -29
  49. package/src/components/PersonAdd.tsx +75 -75
  50. package/src/components/QuestionEdit.tsx +63 -62
  51. package/src/components/SmallButton.tsx +39 -39
  52. package/src/components/SupportModal.tsx +26 -26
  53. package/src/components/TabPanel.tsx +34 -34
  54. package/src/components/gallery/GalleryModal.tsx +102 -102
  55. package/src/components/gallery/StockPhotos.tsx +74 -74
  56. package/src/components/gallery/index.ts +1 -1
  57. package/src/components/iconPicker/IconNamesList.ts +2240 -2240
  58. package/src/components/iconPicker/IconPicker.tsx +153 -153
  59. package/src/components/index.tsx +24 -24
  60. package/src/components/markdownEditor/Editor.tsx +132 -132
  61. package/src/components/markdownEditor/MarkdownEditor.tsx +16 -16
  62. package/src/components/markdownEditor/MarkdownModal.tsx +46 -46
  63. package/src/components/markdownEditor/MarkdownPreview.tsx +14 -14
  64. package/src/components/markdownEditor/editor.css +787 -787
  65. package/src/components/markdownEditor/images/icons/arrow-clockwise.svg +3 -3
  66. package/src/components/markdownEditor/images/icons/arrow-counterclockwise.svg +3 -3
  67. package/src/components/markdownEditor/images/icons/chat-square-quote.svg +3 -3
  68. package/src/components/markdownEditor/images/icons/chevron-down.svg +2 -2
  69. package/src/components/markdownEditor/images/icons/code.svg +2 -2
  70. package/src/components/markdownEditor/images/icons/journal-code.svg +4 -4
  71. package/src/components/markdownEditor/images/icons/journal-text.svg +4 -4
  72. package/src/components/markdownEditor/images/icons/justify.svg +2 -2
  73. package/src/components/markdownEditor/images/icons/link.svg +3 -3
  74. package/src/components/markdownEditor/images/icons/list-ol.svg +3 -3
  75. package/src/components/markdownEditor/images/icons/list-ul.svg +2 -2
  76. package/src/components/markdownEditor/images/icons/pencil-fill.svg +2 -2
  77. package/src/components/markdownEditor/images/icons/text-center.svg +2 -2
  78. package/src/components/markdownEditor/images/icons/text-left.svg +2 -2
  79. package/src/components/markdownEditor/images/icons/text-paragraph.svg +2 -2
  80. package/src/components/markdownEditor/images/icons/text-right.svg +2 -2
  81. package/src/components/markdownEditor/images/icons/type-bold.svg +2 -2
  82. package/src/components/markdownEditor/images/icons/type-h1.svg +2 -2
  83. package/src/components/markdownEditor/images/icons/type-h2.svg +2 -2
  84. package/src/components/markdownEditor/images/icons/type-h3.svg +2 -2
  85. package/src/components/markdownEditor/images/icons/type-h4.svg +12 -12
  86. package/src/components/markdownEditor/images/icons/type-italic.svg +2 -2
  87. package/src/components/markdownEditor/images/icons/type-strikethrough.svg +2 -2
  88. package/src/components/markdownEditor/images/icons/type-underline.svg +2 -2
  89. package/src/components/markdownEditor/index.ts +2 -2
  90. package/src/components/markdownEditor/plugins/AutoLinkPlugin.tsx +35 -35
  91. package/src/components/markdownEditor/plugins/ControlledEditorPlugin.tsx +24 -24
  92. package/src/components/markdownEditor/plugins/ListMaxIndentLevelPlugin.tsx +68 -68
  93. package/src/components/markdownEditor/plugins/MarkdownTransformers.ts +106 -106
  94. package/src/components/markdownEditor/plugins/ReadOnlyPlugin.tsx +15 -15
  95. package/src/components/markdownEditor/plugins/ToolbarPlugin.tsx +401 -401
  96. package/src/components/markdownEditor/plugins/customLink/CustomLinkNode.tsx +224 -224
  97. package/src/components/markdownEditor/plugins/customLink/CustomLinkNodePlugin.tsx +32 -32
  98. package/src/components/markdownEditor/plugins/customLink/CustomLinkNodeTransformer.tsx +102 -102
  99. package/src/components/markdownEditor/plugins/customLink/FloatingLinkEditor.tsx +243 -243
  100. package/src/components/markdownEditor/plugins/customLink/FloatingLinkEditor.types.ts +11 -11
  101. package/src/components/markdownEditor/plugins/emoji/EmojiNode.tsx +95 -95
  102. package/src/components/markdownEditor/plugins/emoji/EmojiNodeTransform.ts +41 -41
  103. package/src/components/markdownEditor/plugins/emoji/EmojiPickerPlugin.tsx +152 -152
  104. package/src/components/markdownEditor/plugins/emoji/EmojisPlugin.tsx +65 -65
  105. package/src/components/markdownEditor/plugins/index.ts +6 -6
  106. package/src/components/markdownEditor/theme.ts +65 -65
  107. package/src/components/notes/AddNote.tsx +90 -90
  108. package/src/components/notes/Conversation.tsx +82 -82
  109. package/src/components/notes/Conversations.tsx +58 -58
  110. package/src/components/notes/NewConversation.tsx +78 -78
  111. package/src/components/notes/Note.tsx +44 -44
  112. package/src/components/notes/Notes.tsx +68 -68
  113. package/src/components/notes/index.ts +5 -5
  114. package/src/components/reporting/ChartReport.tsx +98 -98
  115. package/src/components/reporting/ReportFilter.tsx +54 -54
  116. package/src/components/reporting/ReportFilterField.tsx +160 -160
  117. package/src/components/reporting/ReportOutput.tsx +79 -79
  118. package/src/components/reporting/ReportWithFilter.tsx +70 -70
  119. package/src/components/reporting/TableReport.tsx +57 -57
  120. package/src/components/reporting/TreeReport.tsx +111 -111
  121. package/src/components/reporting/index.ts +4 -4
  122. package/src/components/wrapper/AppList.tsx +20 -20
  123. package/src/components/wrapper/ChurchList.tsx +22 -22
  124. package/src/components/wrapper/Drawers.tsx +60 -60
  125. package/src/components/wrapper/NavItem.tsx +41 -41
  126. package/src/components/wrapper/NewPrivateMessage.tsx +103 -103
  127. package/src/components/wrapper/NotificationMenu.tsx +85 -85
  128. package/src/components/wrapper/Notifications.tsx +50 -50
  129. package/src/components/wrapper/PrivateMessageDetails.tsx +23 -23
  130. package/src/components/wrapper/PrivateMessages.tsx +87 -87
  131. package/src/components/wrapper/SiteWrapper.tsx +96 -91
  132. package/src/components/wrapper/TabPanel.tsx +30 -30
  133. package/src/components/wrapper/UserMenu.tsx +106 -106
  134. package/src/components/wrapper/index.tsx +5 -5
  135. package/src/donationComponents/DonationPage.tsx +136 -136
  136. package/src/donationComponents/components/BankForm.tsx +159 -159
  137. package/src/donationComponents/components/CardForm.tsx +104 -104
  138. package/src/donationComponents/components/DonationForm.tsx +235 -235
  139. package/src/donationComponents/components/FundDonation.tsx +49 -49
  140. package/src/donationComponents/components/FundDonations.tsx +39 -39
  141. package/src/donationComponents/components/NonAuthDonation.tsx +31 -31
  142. package/src/donationComponents/components/NonAuthDonationInner.tsx +259 -259
  143. package/src/donationComponents/components/PaymentMethods.tsx +135 -135
  144. package/src/donationComponents/components/RecurringDonations.tsx +121 -121
  145. package/src/donationComponents/components/RecurringDonationsEdit.tsx +93 -93
  146. package/src/donationComponents/components/index.tsx +9 -9
  147. package/src/donationComponents/index.ts +3 -3
  148. package/src/donationComponents/modals/DonationPreviewModal.tsx +66 -66
  149. package/src/helpers/AnalyticsHelper.ts +33 -33
  150. package/src/helpers/ApiHelper.ts +125 -125
  151. package/src/helpers/AppearanceHelper.ts +69 -69
  152. package/src/helpers/ArrayHelper.ts +81 -81
  153. package/src/helpers/CommonEnvironmentHelper.ts +80 -80
  154. package/src/helpers/CurrencyHelper.ts +10 -10
  155. package/src/helpers/DateHelper.ts +108 -108
  156. package/src/helpers/DonationHelper.ts +26 -26
  157. package/src/helpers/ErrorHelper.ts +36 -36
  158. package/src/helpers/EventHelper.ts +52 -52
  159. package/src/helpers/FileHelper.ts +31 -31
  160. package/src/helpers/PersonHelper.ts +60 -60
  161. package/src/helpers/SocketHelper.ts +76 -76
  162. package/src/helpers/Themes.ts +14 -14
  163. package/src/helpers/UniqueIdHelper.ts +36 -36
  164. package/src/helpers/UserHelper.ts +59 -59
  165. package/src/helpers/createEmotionCache.ts +17 -17
  166. package/src/helpers/index.ts +18 -18
  167. package/src/hooks/index.ts +1 -1
  168. package/src/hooks/useMountedState.ts +16 -16
  169. package/src/index.ts +6 -6
  170. package/src/interfaces/Access.ts +24 -24
  171. package/src/interfaces/Attendance.ts +8 -8
  172. package/src/interfaces/Content.ts +10 -10
  173. package/src/interfaces/Doing.ts +24 -24
  174. package/src/interfaces/Donation.ts +45 -45
  175. package/src/interfaces/Error.ts +17 -17
  176. package/src/interfaces/Membership.ts +51 -51
  177. package/src/interfaces/Messaging.ts +21 -21
  178. package/src/interfaces/Permissions.ts +68 -68
  179. package/src/interfaces/Reporting.ts +7 -7
  180. package/src/interfaces/UserContextInterface.ts +13 -13
  181. package/src/interfaces/index.ts +13 -13
  182. package/src/pageComponents/LoginPage.tsx +244 -244
  183. package/src/pageComponents/LogoutPage.tsx +28 -28
  184. package/src/pageComponents/components/Forgot.tsx +79 -79
  185. package/src/pageComponents/components/Login.tsx +54 -54
  186. package/src/pageComponents/components/LoginSetPassword.tsx +63 -63
  187. package/src/pageComponents/components/Register.tsx +107 -107
  188. package/src/pageComponents/components/SelectChurchModal.tsx +41 -41
  189. package/src/pageComponents/components/SelectChurchRegister.tsx +88 -88
  190. package/src/pageComponents/components/SelectChurchSearch.tsx +69 -69
  191. package/src/pageComponents/components/SelectableChurch.tsx +38 -38
  192. package/src/pageComponents/index.ts +3 -3
  193. package/tsconfig.json +34 -34
  194. 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
+