@churchapps/apphelper 0.1.0 → 0.1.2

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