@churchapps/apphelper 0.6.14 → 0.6.16

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 (215) hide show
  1. package/dist/components/DisplayBox.d.ts +1 -1
  2. package/dist/components/DisplayBox.d.ts.map +1 -1
  3. package/dist/components/DisplayBox.js +2 -4
  4. package/dist/components/DisplayBox.js.map +1 -1
  5. package/dist/components/ErrorMessages.d.ts.map +1 -1
  6. package/dist/components/ErrorMessages.js +1 -1
  7. package/dist/components/ErrorMessages.js.map +1 -1
  8. package/dist/components/ExportLink.d.ts.map +1 -1
  9. package/dist/components/ExportLink.js +10 -10
  10. package/dist/components/ExportLink.js.map +1 -1
  11. package/dist/components/FloatingSupport.js.map +1 -1
  12. package/dist/components/FormCardPayment.d.ts.map +1 -1
  13. package/dist/components/FormCardPayment.js +20 -5
  14. package/dist/components/FormCardPayment.js.map +1 -1
  15. package/dist/components/FormSubmissionEdit.d.ts.map +1 -1
  16. package/dist/components/FormSubmissionEdit.js +8 -10
  17. package/dist/components/FormSubmissionEdit.js.map +1 -1
  18. package/dist/components/HelpIcon.d.ts.map +1 -1
  19. package/dist/components/HelpIcon.js.map +1 -1
  20. package/dist/components/ImageEditor.d.ts.map +1 -1
  21. package/dist/components/ImageEditor.js +10 -10
  22. package/dist/components/ImageEditor.js.map +1 -1
  23. package/dist/components/InputBox.d.ts +2 -1
  24. package/dist/components/InputBox.d.ts.map +1 -1
  25. package/dist/components/InputBox.js +5 -7
  26. package/dist/components/InputBox.js.map +1 -1
  27. package/dist/components/Loading.d.ts.map +1 -1
  28. package/dist/components/Loading.js.map +1 -1
  29. package/dist/components/PageHeader.js +14 -14
  30. package/dist/components/PersonAvatar.d.ts.map +1 -1
  31. package/dist/components/PersonAvatar.js +9 -16
  32. package/dist/components/PersonAvatar.js.map +1 -1
  33. package/dist/components/QuestionEdit.js +3 -3
  34. package/dist/components/QuestionEdit.js.map +1 -1
  35. package/dist/components/SmallButton.d.ts.map +1 -1
  36. package/dist/components/SmallButton.js +2 -1
  37. package/dist/components/SmallButton.js.map +1 -1
  38. package/dist/components/SupportModal.js.map +1 -1
  39. package/dist/components/gallery/GalleryModal.js +10 -10
  40. package/dist/components/gallery/GalleryModal.js.map +1 -1
  41. package/dist/components/gallery/StockPhotos.js +12 -12
  42. package/dist/components/gallery/StockPhotos.js.map +1 -1
  43. package/dist/components/header/Banner.d.ts.map +1 -1
  44. package/dist/components/header/Banner.js.map +1 -1
  45. package/dist/components/header/PrimaryMenu.js +4 -4
  46. package/dist/components/header/PrimaryMenu.js.map +1 -1
  47. package/dist/components/header/SecondaryMenu.d.ts.map +1 -1
  48. package/dist/components/header/SecondaryMenu.js.map +1 -1
  49. package/dist/components/header/SecondaryMenuAlt.d.ts.map +1 -1
  50. package/dist/components/header/SecondaryMenuAlt.js.map +1 -1
  51. package/dist/components/header/SiteHeader.d.ts.map +1 -1
  52. package/dist/components/header/SiteHeader.js +7 -7
  53. package/dist/components/header/SiteHeader.js.map +1 -1
  54. package/dist/components/header/SupportDrawer.js +1 -1
  55. package/dist/components/header/SupportDrawer.js.map +1 -1
  56. package/dist/components/notes/AddNote.js +14 -14
  57. package/dist/components/notes/Note.d.ts.map +1 -1
  58. package/dist/components/notes/Note.js +7 -7
  59. package/dist/components/notes/Note.js.map +1 -1
  60. package/dist/components/notes/Notes.d.ts.map +1 -1
  61. package/dist/components/notes/Notes.js +4 -4
  62. package/dist/components/notes/Notes.js.map +1 -1
  63. package/dist/components/wrapper/AppList.d.ts.map +1 -1
  64. package/dist/components/wrapper/AppList.js.map +1 -1
  65. package/dist/components/wrapper/ChurchList.js +5 -5
  66. package/dist/components/wrapper/ChurchList.js.map +1 -1
  67. package/dist/components/wrapper/NavItem.js +1 -1
  68. package/dist/components/wrapper/NavItem.js.map +1 -1
  69. package/dist/components/wrapper/NewPrivateMessage.d.ts.map +1 -1
  70. package/dist/components/wrapper/NewPrivateMessage.js +7 -6
  71. package/dist/components/wrapper/NewPrivateMessage.js.map +1 -1
  72. package/dist/components/wrapper/Notifications.d.ts.map +1 -1
  73. package/dist/components/wrapper/Notifications.js +12 -14
  74. package/dist/components/wrapper/Notifications.js.map +1 -1
  75. package/dist/components/wrapper/PrivateMessageDetails.js +10 -10
  76. package/dist/components/wrapper/PrivateMessages.d.ts.map +1 -1
  77. package/dist/components/wrapper/PrivateMessages.js +119 -123
  78. package/dist/components/wrapper/PrivateMessages.js.map +1 -1
  79. package/dist/components/wrapper/UserMenu.d.ts.map +1 -1
  80. package/dist/components/wrapper/UserMenu.js +15 -15
  81. package/dist/components/wrapper/UserMenu.js.map +1 -1
  82. package/dist/helpers/AnalyticsHelper.d.ts.map +1 -1
  83. package/dist/helpers/AnalyticsHelper.js +3 -3
  84. package/dist/helpers/AnalyticsHelper.js.map +1 -1
  85. package/dist/helpers/ArrayHelper.d.ts.map +1 -1
  86. package/dist/helpers/ArrayHelper.js.map +1 -1
  87. package/dist/helpers/CurrencyHelper.d.ts +4 -0
  88. package/dist/helpers/CurrencyHelper.d.ts.map +1 -1
  89. package/dist/helpers/CurrencyHelper.js +65 -0
  90. package/dist/helpers/CurrencyHelper.js.map +1 -1
  91. package/dist/helpers/ErrorHelper.d.ts.map +1 -1
  92. package/dist/helpers/ErrorHelper.js.map +1 -1
  93. package/dist/helpers/EventHelper.d.ts.map +1 -1
  94. package/dist/helpers/EventHelper.js +1 -1
  95. package/dist/helpers/EventHelper.js.map +1 -1
  96. package/dist/helpers/FileHelper.d.ts.map +1 -1
  97. package/dist/helpers/FileHelper.js +3 -1
  98. package/dist/helpers/FileHelper.js.map +1 -1
  99. package/dist/helpers/Locale.d.ts.map +1 -1
  100. package/dist/helpers/Locale.js +10 -16
  101. package/dist/helpers/Locale.js.map +1 -1
  102. package/dist/helpers/NotificationService.js +4 -4
  103. package/dist/helpers/PersonHelper.d.ts.map +1 -1
  104. package/dist/helpers/PersonHelper.js +5 -4
  105. package/dist/helpers/PersonHelper.js.map +1 -1
  106. package/dist/helpers/SlugHelper.d.ts.map +1 -1
  107. package/dist/helpers/SlugHelper.js +5 -3
  108. package/dist/helpers/SlugHelper.js.map +1 -1
  109. package/dist/helpers/SocketHelper.d.ts.map +1 -1
  110. package/dist/helpers/SocketHelper.js +5 -10
  111. package/dist/helpers/SocketHelper.js.map +1 -1
  112. package/dist/helpers/UniqueIdHelper.d.ts.map +1 -1
  113. package/dist/helpers/UniqueIdHelper.js +132 -7
  114. package/dist/helpers/UniqueIdHelper.js.map +1 -1
  115. package/dist/helpers/UserHelper.d.ts.map +1 -1
  116. package/dist/helpers/UserHelper.js +2 -2
  117. package/dist/helpers/UserHelper.js.map +1 -1
  118. package/dist/hooks/useMountedState.d.ts.map +1 -1
  119. package/dist/hooks/useMountedState.js +1 -1
  120. package/dist/hooks/useMountedState.js.map +1 -1
  121. package/dist/hooks/useNotifications.d.ts +2 -2
  122. package/dist/hooks/useNotifications.js +5 -5
  123. package/dist/public/locales/de.json +15 -7
  124. package/dist/public/locales/es.json +193 -190
  125. package/dist/public/locales/fr.json +15 -7
  126. package/dist/public/locales/hi.json +15 -7
  127. package/dist/public/locales/it.json +15 -7
  128. package/dist/public/locales/ko.json +15 -7
  129. package/dist/public/locales/no.json +15 -7
  130. package/dist/public/locales/pt.json +15 -7
  131. package/dist/public/locales/ru.json +15 -7
  132. package/dist/public/locales/tl.json +15 -7
  133. package/dist/public/locales/zh.json +15 -7
  134. package/package.json +106 -73
  135. package/dist/helpers/DateHelper.d.ts +0 -18
  136. package/dist/helpers/DateHelper.d.ts.map +0 -1
  137. package/dist/helpers/DateHelper.js +0 -102
  138. package/dist/helpers/DateHelper.js.map +0 -1
  139. package/public/css/cropper.css +0 -309
  140. package/public/css/styles.css +0 -112
  141. package/public/locales/de.json +0 -273
  142. package/public/locales/en.json +0 -281
  143. package/public/locales/es.json +0 -278
  144. package/public/locales/fr.json +0 -273
  145. package/public/locales/hi.json +0 -273
  146. package/public/locales/it.json +0 -273
  147. package/public/locales/ko.json +0 -273
  148. package/public/locales/no.json +0 -273
  149. package/public/locales/pt.json +0 -273
  150. package/public/locales/ru.json +0 -273
  151. package/public/locales/tl.json +0 -273
  152. package/public/locales/zh.json +0 -273
  153. package/src/components/DisplayBox.tsx +0 -83
  154. package/src/components/ErrorMessages.tsx +0 -28
  155. package/src/components/ExportLink.tsx +0 -81
  156. package/src/components/FloatingSupport.tsx +0 -18
  157. package/src/components/FormCardPayment.tsx +0 -184
  158. package/src/components/FormSubmissionEdit.tsx +0 -168
  159. package/src/components/HelpIcon.tsx +0 -12
  160. package/src/components/ImageEditor.tsx +0 -167
  161. package/src/components/InputBox.tsx +0 -96
  162. package/src/components/Loading.tsx +0 -31
  163. package/src/components/PageHeader.tsx +0 -111
  164. package/src/components/PersonAvatar.tsx +0 -78
  165. package/src/components/QuestionEdit.tsx +0 -99
  166. package/src/components/SmallButton.tsx +0 -42
  167. package/src/components/SupportModal.tsx +0 -32
  168. package/src/components/TabPanel.tsx +0 -28
  169. package/src/components/gallery/GalleryModal.tsx +0 -170
  170. package/src/components/gallery/StockPhotos.tsx +0 -96
  171. package/src/components/gallery/index.ts +0 -2
  172. package/src/components/header/Banner.tsx +0 -11
  173. package/src/components/header/PrimaryMenu.tsx +0 -101
  174. package/src/components/header/SecondaryMenu.tsx +0 -23
  175. package/src/components/header/SecondaryMenuAlt.tsx +0 -40
  176. package/src/components/header/SiteHeader.tsx +0 -205
  177. package/src/components/header/SupportDrawer.tsx +0 -112
  178. package/src/components/header/index.tsx +0 -2
  179. package/src/components/index.tsx +0 -20
  180. package/src/components/notes/AddNote.tsx +0 -185
  181. package/src/components/notes/Note.tsx +0 -84
  182. package/src/components/notes/Notes.tsx +0 -208
  183. package/src/components/notes/index.ts +0 -3
  184. package/src/components/wrapper/AppList.tsx +0 -18
  185. package/src/components/wrapper/ChurchList.tsx +0 -145
  186. package/src/components/wrapper/NavItem.tsx +0 -47
  187. package/src/components/wrapper/NewPrivateMessage.tsx +0 -249
  188. package/src/components/wrapper/Notifications.tsx +0 -220
  189. package/src/components/wrapper/PrivateMessageDetails.tsx +0 -106
  190. package/src/components/wrapper/PrivateMessages.tsx +0 -574
  191. package/src/components/wrapper/UserMenu.tsx +0 -384
  192. package/src/components/wrapper/index.tsx +0 -8
  193. package/src/helpers/AnalyticsHelper.ts +0 -44
  194. package/src/helpers/AppearanceHelper.ts +0 -73
  195. package/src/helpers/ArrayHelper.ts +0 -87
  196. package/src/helpers/CurrencyHelper.ts +0 -10
  197. package/src/helpers/DateHelper.ts +0 -104
  198. package/src/helpers/ErrorHelper.ts +0 -41
  199. package/src/helpers/EventHelper.ts +0 -49
  200. package/src/helpers/FileHelper.ts +0 -31
  201. package/src/helpers/Locale.ts +0 -457
  202. package/src/helpers/NotificationService.ts +0 -233
  203. package/src/helpers/PersonHelper.ts +0 -62
  204. package/src/helpers/SlugHelper.ts +0 -27
  205. package/src/helpers/SocketHelper.ts +0 -183
  206. package/src/helpers/UniqueIdHelper.ts +0 -36
  207. package/src/helpers/UserHelper.ts +0 -103
  208. package/src/helpers/createEmotionCache.ts +0 -17
  209. package/src/helpers/index.ts +0 -58
  210. package/src/hooks/index.ts +0 -3
  211. package/src/hooks/useMountedState.ts +0 -18
  212. package/src/hooks/useNotifications.ts +0 -95
  213. package/src/index.ts +0 -3
  214. package/src/types/interface-extensions.d.ts +0 -12
  215. package/tsconfig.json +0 -32
@@ -1,81 +0,0 @@
1
- "use client";
2
-
3
- import { Button, Icon } from "@mui/material";
4
- import React, { Suspense, lazy } from "react";
5
- import { Loading } from "./Loading";
6
-
7
- // Lazy load the CSVLink component
8
- const CSVLink = lazy(() => import("react-csv").then(module => ({ default: module.CSVLink })));
9
-
10
- interface Props {
11
- data: any[],
12
- spaceAfter?: boolean,
13
- spaceBefore?: boolean,
14
- filename?: string,
15
- icon?: string,
16
- text?: string,
17
- customHeaders?: {label: string, key: string}[]
18
- }
19
-
20
- export const ExportLink: React.FC<Props> = (props) => {
21
-
22
- const people = props.data ? [...props.data] : [];
23
-
24
- const getHeaders = () => {
25
- let result = [];
26
- if (people?.length > 0) {
27
- let names = getAllPropertyNames();
28
- for (let i = 0; i < names.length; i++) { result.push({ label: names[i], key: names[i] }); }
29
- }
30
- return result;
31
- }
32
-
33
- const getAllPropertyNames = () => {
34
- let result = [];
35
- for (let i = 0; i < people.length; i++) {
36
- let p = {...people[i]}
37
- p.birthDate = p.birthDate ? new Date(p.birthDate).toISOString() : null;
38
- p.anniversary = p.anniversary ? new Date(p.anniversary).toISOString() : null;
39
- people[i] = p;
40
- let propertyNames = getPropertyNames("", people[i]);
41
- for (let j = 0; j < propertyNames.length; j++) if (result.indexOf(propertyNames[j]) === -1) result.push(propertyNames[j]);
42
- }
43
- return result.sort();
44
- }
45
-
46
- const getPropertyNames = (prefix: string, obj: any) => {
47
- let result = [];
48
- let names = Object.getOwnPropertyNames(obj)
49
- for (let i = 0; i < names.length; i++) {
50
- let t = typeof obj[names[i]];
51
- switch (t) {
52
- case "number":
53
- case "string":
54
- case "boolean":
55
- result.push(prefix + names[i]);
56
- break;
57
- case "object":
58
- if ((obj[names[i]] !== null)) {
59
- let children: string[] = getPropertyNames(prefix + names[i] + ".", obj[names[i]]);
60
- for (let j = 0; j < children.length; j++) result.push(children[j]);
61
- }
62
- }
63
- }
64
- return result;
65
- }
66
-
67
- if (!people || people?.length === 0) return null;
68
- else {
69
- let items = [];
70
- if (props.spaceBefore) items.push(" ");
71
- items.push(
72
- <Suspense key={props.filename} fallback={<Button><Icon sx={{ marginRight: props.text ? 1 : 0 }}>{props.icon || "file_download"}</Icon>{props.text || ""}</Button>}>
73
- <CSVLink data={people} headers={props.customHeaders || getHeaders()} filename={props.filename || "export.csv"}>
74
- <Button><Icon sx={{ marginRight: props.text ? 1 : 0 }}>{props.icon || "file_download"}</Icon>{props.text || ""}</Button>
75
- </CSVLink>
76
- </Suspense>
77
- );
78
- if (props.spaceAfter) items.push(" ");
79
- return (<>{items}</>);
80
- }
81
- }
@@ -1,18 +0,0 @@
1
- "use client";
2
-
3
- import { Fab, Icon } from "@mui/material";
4
- import React from "react";
5
- import { SupportModal } from "./SupportModal";
6
-
7
- interface Props { appName?: string }
8
-
9
- export const FloatingSupport: React.FC<Props> = (props) => {
10
- const [showSupport, setShowSupport] = React.useState(false);
11
-
12
- return (<>
13
- <Fab color="primary" style={{ position: "fixed", cursor: "pointer", bottom: "20px", right: (showSupport) ? 47 : 30 }} onClick={() => { setShowSupport(!showSupport) }} aria-label="Support">
14
- <Icon style={{ fontSize: 40 }}>contact_support</Icon>
15
- </Fab>
16
- {showSupport && <SupportModal onClose={() => setShowSupport(false)} appName={props.appName} />}
17
- </>);
18
- };
@@ -1,184 +0,0 @@
1
- "use client";
2
-
3
- import React, { forwardRef, useImperativeHandle } from "react";
4
- import { CardElement, useElements, useStripe } from "@stripe/react-stripe-js";
5
- import { Box, Grid, TextField } from "@mui/material";
6
- import { ApiHelper, Locale, UserInterface, PersonInterface, StripeDonationInterface, ChurchInterface, FundInterface, ArrayHelper, UserHelper, QuestionInterface } from "../helpers";
7
- import { StripePaymentMethod } from "@churchapps/helpers";
8
-
9
- interface Props {
10
- churchId: string,
11
- question: QuestionInterface
12
- }
13
-
14
- export const FormCardPayment = forwardRef((props: Props, ref) => {
15
- const formStyling = { style: { base: { fontSize: "18px" } } };
16
- const elements = useElements();
17
- const stripe = useStripe();
18
- const [email, setEmail] = React.useState<string>((ApiHelper.isAuthenticated && UserHelper.user.email) ? UserHelper.user.email : "");
19
- const [firstName, setFirstName] = React.useState<string>((ApiHelper.isAuthenticated && UserHelper.user.firstName) ? UserHelper.user.firstName : "");
20
- const [lastName, setLastName] = React.useState<string>((ApiHelper.isAuthenticated && UserHelper.user.lastName) ? UserHelper.user.lastName : "");
21
- const [church, setChurch] = React.useState<ChurchInterface>();
22
- const [fund, setFund] = React.useState<FundInterface>();
23
- const [gateway, setGateway] = React.useState<any>(null);
24
- let amt = Number(props.question.choices?.find((c: any) => c.text === "Amount")?.value);
25
- let fundId = props.question.choices?.find((c: any) => c.text === "FundId")?.value;
26
-
27
- const getChurchData = () => {
28
- let fundId = props.question.choices?.find((c: any) => c.text === "FundId")?.value;
29
- ApiHelper.get("/churches/" + props.churchId, "MembershipApi").then((data: any) => {
30
- setChurch(data);
31
- });
32
- ApiHelper.get("/funds/churchId/" + props.churchId, "GivingApi").then((data: any) => {
33
- const result = ArrayHelper.getOne(data, "id", fundId);
34
- setFund(result);
35
- });
36
- ApiHelper.get(`/donate/gateways/${props.churchId}`, "GivingApi").then((response: any) => {
37
- const gateways = Array.isArray(response?.gateways) ? response.gateways : [];
38
- const stripeGateway = gateways.find((g: any) => g.provider?.toLowerCase() === "stripe");
39
- if (stripeGateway) setGateway(stripeGateway);
40
- });
41
- }
42
-
43
- const handlePayment = async () => {
44
- const validateErrors = validate();
45
- if (validateErrors.length > 0) {
46
- return { paymentSuccessful: false, errors: validateErrors }
47
- }
48
-
49
- try {
50
- const userData = await ApiHelper.post("/users/loadOrCreate", { userEmail: email, firstName, lastName }, "MembershipApi");
51
-
52
- const personData = { churchId: props.churchId, firstName, lastName, email };
53
- const person = await ApiHelper.post("/people/loadOrCreate", personData, "MembershipApi");
54
-
55
- const cardSavedRes = await saveCard(userData, person);
56
- if (!cardSavedRes.success) {
57
- return { paymentSuccessful: false, name: "", errors: cardSavedRes.errors }
58
- }
59
-
60
- return { paymentSuccessful: true, name: person?.name?.display || "", errors: [] as string[] }
61
- } catch (err) {
62
- const errorMessage = "An error occurred while processing your payment.";
63
- return { paymentSuccessful: false, name: "", errors: [errorMessage] as string[] }
64
- }
65
- }
66
-
67
- const saveCard = async (user: UserInterface, person: PersonInterface) => {
68
- const cardData = elements.getElement(CardElement);
69
- try {
70
- const stripePM = await stripe.createPaymentMethod({ type: "card", card: cardData });
71
- if (stripePM.error) {
72
- return { success: false, errors: [stripePM.error.message] };
73
- } else {
74
- const pm = {
75
- id: stripePM.paymentMethod.id,
76
- personId: person.id,
77
- email: email,
78
- name: person.name.display,
79
- churchId: props.churchId,
80
- provider: "stripe",
81
- gatewayId: gateway?.id
82
- };
83
- try {
84
- const result = await ApiHelper.post("/paymentmethods/addcard", pm, "GivingApi");
85
- if (result?.raw?.message) {
86
- return { success: false, errors: [result.raw.message] };
87
- } else {
88
- const p: { paymentMethod: StripePaymentMethod, customerId: string } = result
89
- const savedPaymentRes = await savePayment(p.paymentMethod, p.customerId, person);
90
- if (!savedPaymentRes.success) {
91
- return { success: false, errors: savedPaymentRes.errors }
92
- }
93
- return { success: true, errors: [] }
94
- }
95
- } catch (apiError) {
96
- return { success: false, errors: ["An error occurred while saving the card."] }
97
- }
98
- }
99
- } catch (stripeError) {
100
- return { success: false, errors: ["An error occurred while processing your payment method."] };
101
- }
102
- }
103
-
104
- const savePayment = async (paymentMethod: StripePaymentMethod, customerId: string, person?: PersonInterface) => {
105
-
106
- let payment: any = {
107
- amount: amt,
108
- id: paymentMethod.id,
109
- customerId: customerId,
110
- type: paymentMethod.type,
111
- churchId: props.churchId,
112
- funds: [{ id: fundId, amount: amt, name: fund?.name }],
113
- person: {
114
- id: person?.id,
115
- email: person?.contactInfo?.email,
116
- name: person?.name?.display,
117
- },
118
- provider: "stripe",
119
- gatewayId: gateway?.id || (paymentMethod as any).gatewayId
120
- }
121
-
122
- const churchObj = {
123
- name: church?.name,
124
- subDomain: church?.subDomain,
125
- churchURL: typeof window !== "undefined" && window.location.origin,
126
- logo: ""
127
- }
128
-
129
- try {
130
- const result = await ApiHelper.post("/donate/charge", { ...payment, church: churchObj }, "GivingApi");
131
- if (result?.status === "succeeded" || result?.status === "pending") {
132
- return { success: true, errors: [] }
133
- }
134
-
135
- if (result?.raw?.message) {
136
- return { success: false, errors: [result.raw.message] }
137
- }
138
-
139
- } catch (err) {
140
- return { success: false, errors: ["An error occurred while saving your payment."] }
141
- }
142
-
143
- }
144
-
145
- const validate = () => {
146
- const result = [];
147
- if (!firstName) result.push(Locale.label("donation.donationForm.validate.firstName"));
148
- if (!lastName) result.push(Locale.label("donation.donationForm.validate.lastName"));
149
- if (!email) result.push(Locale.label("donation.donationForm.validate.email"));
150
- if (result.length === 0) {
151
- if (!email.match(/^[^\s@]+@[^\s@]+\.[^\s@]+$/)) result.push(Locale.label("donation.donationForm.validate.validEmail"));
152
- }
153
- return result;
154
- }
155
-
156
- useImperativeHandle(ref, () => ({
157
- handlePayment,
158
- questionId: props.question.id
159
- }));
160
-
161
- React.useEffect(getChurchData, []);
162
-
163
- return <div style={{ backgroundColor: "#bdbdbd", padding: 35, borderRadius: 20 }}>
164
- <Grid container spacing={2}>
165
- <Grid size={12}>
166
- <TextField fullWidth required size="small" margin="none" style={{backgroundColor: "white", borderRadius: "4px"}} InputLabelProps={{ sx: { fontWeight: "bold" } }} label={Locale.label("person.email")} value={email} onChange={(e) => setEmail(e.target.value)} disabled={ApiHelper.isAuthenticated && UserHelper.user.email !== ""} />
167
- </Grid>
168
- <Grid size={{ xs: 12, sm: 6 }}>
169
- <TextField fullWidth required size="small" margin="none" style={{backgroundColor: "white", borderRadius: "4px"}} InputLabelProps={{ sx: { fontWeight: "bold" } }} label={Locale.label("person.firstName")} value={firstName} onChange={(e) => setFirstName(e.target.value)} disabled={ApiHelper.isAuthenticated && UserHelper.user.firstName !== ""} />
170
- </Grid>
171
- <Grid size={{ xs: 12, sm: 6 }}>
172
- <TextField fullWidth required size="small" margin="none" style={{backgroundColor: "white", borderRadius: "4px"}} InputLabelProps={{ sx: { fontWeight: "bold" } }} label={Locale.label("person.lastName")} value={lastName} onChange={(e) => setLastName(e.target.value)} disabled={ApiHelper.isAuthenticated && UserHelper.user.lastName !== ""} />
173
- </Grid>
174
- <Grid size={12}>
175
- <Box sx={{ backgroundColor: "white", padding: 1.5, borderRadius: 1, color: "gray", fontWeight: "bold", fontSize: 18 }}>$ {amt}</Box>
176
- </Grid>
177
- <Grid size={12}>
178
- <div style={{ padding: 10, border: "1px solid #CCC", borderRadius: 5, backgroundColor: "white" }}>
179
- <CardElement options={formStyling} />
180
- </div>
181
- </Grid>
182
- </Grid>
183
- </div>
184
- });
@@ -1,168 +0,0 @@
1
- "use client";
2
-
3
- import React, { useRef } from "react";
4
- import { loadStripe, Stripe } from "@stripe/stripe-js";
5
- import { ErrorMessages, InputBox, QuestionEdit } from "./";
6
- import { ApiHelper, Locale, UniqueIdHelper, UserHelper } from "../helpers";
7
- import { AnswerInterface, QuestionInterface, FormSubmissionInterface } from "@churchapps/helpers";
8
-
9
- interface Props {
10
- addFormId: string,
11
- contentType: string,
12
- contentId: string,
13
- formSubmissionId: string,
14
- unRestrictedFormId?: string,
15
- personId?: string,
16
- churchId?: string,
17
- showHeader?: boolean,
18
- noBackground?:boolean,
19
- updatedFunction: () => void,
20
- cancelFunction?: () => void
21
- }
22
-
23
- export const FormSubmissionEdit: React.FC<Props> = ({showHeader = true, noBackground = false, ...props}) => {
24
- const [stripePromise, setStripe] = React.useState<Promise<Stripe>>(null);
25
- const [formSubmission, setFormSubmission] = React.useState(null);
26
- const [errors, setErrors] = React.useState([]);
27
- const [isSubmitting, setIsSubmitting] = React.useState<boolean>(false);
28
- const paymentRef = useRef<any>(null);
29
-
30
- const getDeleteFunction = () => (!UniqueIdHelper.isMissing(formSubmission?.id)) ? handleDelete : undefined
31
- const handleDelete = () => {
32
- if (window.confirm(Locale.label("formSubmissionEdit.confirmDelete"))) {
33
- ApiHelper.delete("/formsubmissions/" + formSubmission.id, "MembershipApi").then(() => {
34
- props.updatedFunction();
35
- });
36
- }
37
- }
38
-
39
- const getAnswer = (questionId: string) => {
40
- let answers = formSubmission.answers;
41
- for (let i = 0; i < answers.length; i++) if (answers[i].questionId === questionId) return answers[i];
42
- return null;
43
- }
44
-
45
- const setFormSubmissionData = (data: any) => {
46
- const formId = props.addFormId || props.unRestrictedFormId;
47
- let fs: FormSubmissionInterface = {
48
- formId, contentType: props.contentType, contentId: props.contentId, answers: []
49
- };
50
- fs.questions = data;
51
- fs.answers = [];
52
- fs.questions.forEach((q) => {
53
- let answer: AnswerInterface = { formSubmissionId: fs.id, questionId: q.id, required: q.required };
54
- answer.value = getDefaultValue(q);
55
- fs.answers.push(answer);
56
- });
57
- setFormSubmission(fs);
58
- }
59
-
60
- const loadData = () => {
61
- if (!UniqueIdHelper.isMissing(props.formSubmissionId)) ApiHelper.get("/formsubmissions/" + props.formSubmissionId + "/?include=questions,answers,form", "MembershipApi").then((data: any) => setFormSubmission(data));
62
- else if (!UniqueIdHelper.isMissing(props.addFormId)) ApiHelper.get("/questions/?formId=" + props.addFormId, "MembershipApi").then((data: any) => setFormSubmissionData(data));
63
- else if (!UniqueIdHelper.isMissing(props.unRestrictedFormId)) ApiHelper.get("/questions/unrestricted?formId=" + props.unRestrictedFormId, "MembershipApi").then((data: any) => setFormSubmissionData(data));
64
- }
65
-
66
- const getDefaultValue = (q: QuestionInterface) => {
67
- let result = "";
68
- if (q.fieldType === "Yes/No") result = "False";
69
- else if (q.fieldType === "Multiple Choice") {
70
- if (q.choices !== undefined && q.choices !== null && q.choices.length > 0) result = q.choices[0].value;
71
- }
72
- return result;
73
- }
74
-
75
- const validate = (fs: any) => {
76
- let e: any = [];
77
- fs.answers.forEach((a: AnswerInterface) => {
78
-
79
- if (a.required && a.value === "") {
80
- const q: QuestionInterface = fs.questions.find((q: QuestionInterface) => q.id === a.questionId);
81
- e.push(q.title + " " + Locale.label("formSubmissionEdit.isRequired"));
82
- setErrors(e);
83
- }
84
- });
85
- return e.length === 0;
86
- }
87
-
88
- const handleSave = async () => {
89
- const fs = formSubmission;
90
- if (validate(fs)) {
91
- setIsSubmitting(true);
92
- // First, handle the payment if there's a payment component
93
- if (paymentRef.current) {
94
- const paymentResult = await paymentRef.current.handlePayment();
95
- if (!paymentResult.paymentSuccessful) {
96
- setErrors(paymentResult.errors);
97
- setIsSubmitting(false);
98
- return;
99
- } else {
100
- // Mark payment as successful in answers
101
- const paymentAnswer = fs.answers.find((a: AnswerInterface) => a.questionId === paymentRef.current.questionId);
102
- if (paymentAnswer) {
103
- paymentAnswer.value = `Payment Successful [${paymentResult?.name}]`;
104
- } else {
105
- fs.answers.push({
106
- questionId: paymentRef.current.questionId,
107
- value: "Payment Successful"
108
- });
109
- }
110
- }
111
- }
112
-
113
- // If payment is successful or there's no payment, proceed with form submission
114
- fs.submittedBy = props.personId || null;
115
- fs.submissionDate = new Date();
116
- fs.churchId = props.churchId || null;
117
-
118
- ApiHelper.post("/formsubmissions/", [fs], "MembershipApi").then((res: any) => {
119
- if (res?.[0]?.error) {
120
- setErrors([res?.[0].error]);
121
- } else {
122
- props.updatedFunction();
123
- }
124
- })
125
- .finally(() => {
126
- setIsSubmitting(false);
127
- });
128
- }
129
- }
130
-
131
- const handleChange = (questionId: string, value: string) => {
132
- let fs = { ...formSubmission };
133
- let answer: AnswerInterface = null;
134
- for (let i = 0; i < fs.answers.length; i++) if (fs.answers[i].questionId === questionId) answer = fs.answers[i];
135
- if (answer !== null) answer.value = value;
136
- else {
137
- answer = { formSubmissionId: fs.id, questionId: questionId, value: value };
138
- fs.answers.push(answer);
139
- }
140
- setFormSubmission(fs);
141
- }
142
-
143
- React.useEffect(() => {
144
- if (props.churchId) {
145
- ApiHelper.get("/gateways/churchId/" + props.churchId, "GivingApi").then((data: any) => {
146
- if (data.length && data[0]?.publicKey) {
147
- setStripe(loadStripe(data[0].publicKey));
148
- }
149
- });
150
- }
151
- }, [props.churchId]);
152
-
153
- React.useEffect(loadData, []); //eslint-disable-line
154
-
155
- let questionList = [];
156
- if (formSubmission != null) {
157
- let questions = formSubmission.questions;
158
- for (let i = 0; i < questions.length; i++) questionList.push(<QuestionEdit noBackground={noBackground} key={questions[i].id} question={questions[i]} answer={getAnswer(questions[i].id)} changeFunction={handleChange} churchId={props.churchId} ref={questions[i].fieldType === "Payment" ? paymentRef : null} stripePromise={stripePromise} />);
159
- }
160
-
161
- return (
162
- <InputBox id="formSubmissionBox" headerText={showHeader ? (formSubmission?.form?.name || Locale.label("formSubmissionEdit.editForm")) : ""} headerIcon={showHeader ? "person" : ""} mainContainerCssProps={noBackground ? { sx: {backgroundColor: "transparent", boxShadow: 0}}: {}} saveFunction={handleSave} isSubmitting={isSubmitting} saveText={props.contentType === "form" ? Locale.label("formSubmissionEdit.submit") : ""} cancelFunction={props.cancelFunction} deleteFunction={getDeleteFunction()}>
163
- <ErrorMessages errors={errors} />
164
- {questionList}
165
- </InputBox>
166
- );
167
- }
168
-
@@ -1,12 +0,0 @@
1
- "use client";
2
-
3
- import React from "react";
4
- import { SmallButton } from "./SmallButton";
5
-
6
- interface Props { article: string; }
7
-
8
- export const HelpIcon = (props: Props) => (
9
- <span style={{ float: "right", color: "#1976d2" }}>
10
- <SmallButton icon="help" aria-label={"help"} onClick={() => window.open("https://support.churchapps.org/" + props.article, "_blank")} />
11
- </span>
12
- )
@@ -1,167 +0,0 @@
1
- "use client";
2
-
3
- import React, { useState, useEffect, useRef, Suspense, lazy } from "react";
4
- import { InputBox, SmallButton, Loading } from ".";
5
- import { Locale } from "../helpers";
6
- import "../../public/css/cropper.css";
7
-
8
- // Lazy load the Cropper component
9
- const Cropper = lazy(() => import("react-cropper").then(module => ({ default: module.default })));
10
-
11
- interface Props {
12
- title?: string;
13
- photoUrl: string;
14
- aspectRatio: number;
15
- onUpdate: (dataUrl?: string) => void;
16
- onCancel?: () => void;
17
- outputWidth?: number;
18
- outputHeight?: number;
19
- hideDelete?: boolean;
20
- }
21
-
22
- export function ImageEditor(props: Props) {
23
- const [photoSrc, setPhotoSrc] = useState<string>("");
24
- const [croppedImageDataUrl, setCroppedImageDataUrl] = useState<string>("");
25
- const cropperRef = useRef<HTMLImageElement>(null);
26
- const timeoutRef = useRef<number | null>(null);
27
-
28
- // Cleanup timeout on unmount
29
- useEffect(() => {
30
- return () => {
31
- if (timeoutRef.current !== null) {
32
- window.clearTimeout(timeoutRef.current);
33
- }
34
- };
35
- }, []);
36
-
37
- const handleSave = () => {
38
- props.onUpdate(croppedImageDataUrl);
39
- };
40
-
41
- const handleDelete = () => props.onUpdate("");
42
-
43
- const handleUpload = (e: React.ChangeEvent<HTMLInputElement>) => {
44
- e.preventDefault();
45
- let files;
46
- if (e.target) {
47
- files = e.target.files;
48
- }
49
- if (!files || files.length === 0) return;
50
-
51
- const reader = new FileReader();
52
- reader.onload = () => {
53
- const url = reader.result.toString();
54
- setPhotoSrc(url);
55
- setCroppedImageDataUrl("");
56
- };
57
- reader.readAsDataURL(files[0]);
58
-
59
- // Clear the input value to allow re-uploading the same file
60
- e.target.value = "";
61
- };
62
-
63
- const selectDefaultCropZone = () => {
64
- const imageElement: any = cropperRef?.current;
65
- let cropper: any = imageElement?.cropper;
66
- if (props.aspectRatio===0)
67
- {
68
- let containerData = cropper.getContainerData();
69
- const imgWidth = cropper.getImageData().width;
70
- const imgHeight = cropper.getImageData().height;
71
- const effectiveWidth = (containerData.width > imgWidth) ? imgWidth : containerData.width;
72
- const effectiveHeight = (containerData.height > imgHeight) ? imgHeight : containerData.height;
73
- cropper.setCropBoxData({ width: effectiveWidth, height: effectiveHeight, left: (containerData.width - effectiveWidth) / 2.0 , top: (containerData.height - effectiveHeight) / 2.0 });
74
- } else {
75
- let desiredAspect = props.aspectRatio;
76
- let containerData = cropper.getContainerData();
77
- let imgAspect = cropper.getImageData().aspectRatio;
78
- let scale = imgAspect / desiredAspect;
79
- if (scale < 1) {
80
- const imgWidth = cropper.getImageData().width;
81
- let l = (containerData.width - imgWidth) / 2.0;
82
- let t = (containerData.height - (containerData.height * scale)) / 2.0;
83
- cropper.setCropBoxData({ width: imgWidth, height: imgWidth / desiredAspect, left: l, top: t });
84
- } else {
85
- const imgHeight = cropper.getImageData().height;
86
- let l = (containerData.width - (imgHeight * desiredAspect)) / 2.0;
87
- let t = cropper.canvasData.top;
88
- cropper.setCropBoxData({ width: imgHeight * desiredAspect, height: imgHeight, left: l, top: t });
89
- }
90
- }
91
-
92
- }
93
-
94
- const handleCrop = () => {
95
- if (timeoutRef.current !== null) {
96
- window.clearTimeout(timeoutRef.current);
97
- timeoutRef.current = null;
98
- }
99
-
100
- timeoutRef.current = window.setTimeout(() => {
101
- if (cropperRef.current !== null) {
102
- const imageElement: any = cropperRef?.current;
103
- const cropper: any = imageElement?.cropper;
104
-
105
- //Do not enlarge if less
106
- const imageData = cropper.getImageData();
107
- let width = props.outputWidth || 400;
108
- let height = props.outputHeight || 300;
109
- if (imageData.naturalWidth < width && imageData.naturalHeight < height) {
110
- width = imageData.naturalWidth;
111
- height = imageData.naturalHeight;
112
- }
113
-
114
- const url = cropper.getCroppedCanvas({ width, height }).toDataURL("image/png", 0.4);
115
- setCroppedImageDataUrl(url);
116
- }
117
- }, 200);
118
- };
119
-
120
- useEffect(() => { setPhotoSrc(props.photoUrl); }, [props.photoUrl]);
121
-
122
- return (
123
- <InputBox
124
- id="cropperBox"
125
- headerIcon=""
126
- headerText={props.title}
127
- ariaLabelDelete="deletePhoto"
128
- saveText={Locale.label("common.update")}
129
- saveFunction={handleSave}
130
- cancelFunction={props.onCancel}
131
- deleteFunction={(!props.hideDelete) && handleDelete}
132
- headerActionContent={
133
- <div>
134
- <input type="file" onChange={handleUpload} id="fileUpload" accept="image/*" style={{ display: "none" }} />
135
- <SmallButton
136
- icon="upload"
137
- text="Upload"
138
- onClick={() => {
139
- document.getElementById("fileUpload").click();
140
- }}
141
- />
142
- </div>
143
- }
144
- >
145
- <Suspense fallback={<Loading />}>
146
- <Cropper
147
- ref={cropperRef}
148
- src={photoSrc}
149
- key={photoSrc}
150
- style={{ height: 240, width: "100%" }}
151
- aspectRatio={props.aspectRatio}
152
- guides={false}
153
- crop={handleCrop}
154
- autoCropArea={1}
155
- viewMode={0}
156
- responsive={true}
157
- restore={false}
158
- checkOrientation={false}
159
- background={false}
160
- ready={() => {
161
- setTimeout(selectDefaultCropZone, 100);
162
- }}
163
- />
164
- </Suspense>
165
- </InputBox>
166
- );
167
- }