@churchapps/apphelper 0.4.7 → 0.4.9

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 (174) hide show
  1. package/CHUMS_COMPONENT_EXTRACTION_CANDIDATES.md +210 -0
  2. package/COMPONENT_USAGE_REPORT.md +322 -0
  3. package/b1app-specific-components.md +146 -0
  4. package/component-usage-analysis.md +169 -0
  5. package/dist/components/ImageEditor.d.ts +1 -1
  6. package/dist/components/ImageEditor.d.ts.map +1 -1
  7. package/dist/components/ImageEditor.js +1 -1
  8. package/dist/components/ImageEditor.js.map +1 -1
  9. package/dist/components/header/SecondaryMenu.js +3 -3
  10. package/dist/components/header/SecondaryMenu.js.map +1 -1
  11. package/dist/components/index.d.ts +0 -5
  12. package/dist/components/index.d.ts.map +1 -1
  13. package/dist/components/index.js +1 -10
  14. package/dist/components/index.js.map +1 -1
  15. package/dist/components/markdownEditor/IconNamesList.d.ts.map +1 -0
  16. package/dist/components/markdownEditor/IconNamesList.js +16 -0
  17. package/dist/components/markdownEditor/IconNamesList.js.map +1 -0
  18. package/dist/components/markdownEditor/plugins/customLink/CustomLinkNodeTransformer.js +1 -1
  19. package/dist/components/markdownEditor/plugins/customLink/CustomLinkNodeTransformer.js.map +1 -1
  20. package/dist/components/markdownEditor/plugins/emoji/EmojiNodeTransform.js +1 -1
  21. package/dist/components/markdownEditor/plugins/emoji/EmojiNodeTransform.js.map +1 -1
  22. package/dist/components/markdownEditor/plugins/emoji/EmojiPickerPlugin.js +1 -1
  23. package/dist/components/markdownEditor/plugins/emoji/EmojiPickerPlugin.js.map +1 -1
  24. package/dist/components/markdownEditor/plugins/emoji/EmojisPlugin.js +1 -1
  25. package/dist/components/markdownEditor/plugins/emoji/EmojisPlugin.js.map +1 -1
  26. package/dist/components/notes/index.d.ts +0 -2
  27. package/dist/components/notes/index.d.ts.map +1 -1
  28. package/dist/components/notes/index.js +1 -5
  29. package/dist/components/notes/index.js.map +1 -1
  30. package/dist/helpers/FileHelper.d.ts +1 -1
  31. package/dist/helpers/FileHelper.d.ts.map +1 -1
  32. package/dist/helpers/FileHelper.js +2 -6
  33. package/dist/helpers/FileHelper.js.map +1 -1
  34. package/dist/index.d.ts +0 -1
  35. package/dist/index.d.ts.map +1 -1
  36. package/dist/index.js +0 -1
  37. package/dist/index.js.map +1 -1
  38. package/package.json +1 -1
  39. package/src/components/ImageEditor.tsx +1 -1
  40. package/src/components/header/SecondaryMenu.tsx +3 -3
  41. package/src/components/index.tsx +0 -5
  42. package/src/components/markdownEditor/IconNamesList.ts +14 -0
  43. package/src/components/markdownEditor/plugins/customLink/CustomLinkNodeTransformer.tsx +1 -1
  44. package/src/components/markdownEditor/plugins/emoji/EmojiNodeTransform.ts +1 -1
  45. package/src/components/markdownEditor/plugins/emoji/EmojiPickerPlugin.tsx +1 -1
  46. package/src/components/markdownEditor/plugins/emoji/EmojisPlugin.tsx +1 -1
  47. package/src/components/notes/index.ts +0 -2
  48. package/src/helpers/FileHelper.ts +3 -6
  49. package/src/index.ts +0 -1
  50. package/dist/components/B1ShareModal.d.ts +0 -9
  51. package/dist/components/B1ShareModal.d.ts.map +0 -1
  52. package/dist/components/B1ShareModal.js +0 -77
  53. package/dist/components/B1ShareModal.js.map +0 -1
  54. package/dist/components/CreatePerson.d.ts +0 -15
  55. package/dist/components/CreatePerson.d.ts.map +0 -1
  56. package/dist/components/CreatePerson.js +0 -100
  57. package/dist/components/CreatePerson.js.map +0 -1
  58. package/dist/components/PersonAdd.d.ts +0 -16
  59. package/dist/components/PersonAdd.d.ts.map +0 -1
  60. package/dist/components/PersonAdd.js +0 -52
  61. package/dist/components/PersonAdd.js.map +0 -1
  62. package/dist/components/gallery/GalleryModal.d.ts +0 -9
  63. package/dist/components/gallery/GalleryModal.d.ts.map +0 -1
  64. package/dist/components/gallery/GalleryModal.js +0 -111
  65. package/dist/components/gallery/GalleryModal.js.map +0 -1
  66. package/dist/components/gallery/StockPhotos.d.ts +0 -9
  67. package/dist/components/gallery/StockPhotos.d.ts.map +0 -1
  68. package/dist/components/gallery/StockPhotos.js +0 -79
  69. package/dist/components/gallery/StockPhotos.js.map +0 -1
  70. package/dist/components/gallery/index.d.ts +0 -2
  71. package/dist/components/gallery/index.d.ts.map +0 -1
  72. package/dist/components/gallery/index.js +0 -6
  73. package/dist/components/gallery/index.js.map +0 -1
  74. package/dist/components/iconPicker/IconNamesList.d.ts.map +0 -1
  75. package/dist/components/iconPicker/IconNamesList.js +0 -2241
  76. package/dist/components/iconPicker/IconNamesList.js.map +0 -1
  77. package/dist/components/iconPicker/IconPicker.d.ts +0 -6
  78. package/dist/components/iconPicker/IconPicker.d.ts.map +0 -1
  79. package/dist/components/iconPicker/IconPicker.js +0 -142
  80. package/dist/components/iconPicker/IconPicker.js.map +0 -1
  81. package/dist/components/notes/Conversation.d.ts +0 -10
  82. package/dist/components/notes/Conversation.d.ts.map +0 -1
  83. package/dist/components/notes/Conversation.js +0 -55
  84. package/dist/components/notes/Conversation.js.map +0 -1
  85. package/dist/components/notes/Conversations.d.ts +0 -10
  86. package/dist/components/notes/Conversations.d.ts.map +0 -1
  87. package/dist/components/notes/Conversations.js +0 -54
  88. package/dist/components/notes/Conversations.js.map +0 -1
  89. package/dist/components/notes/NewConversation.d.ts +0 -12
  90. package/dist/components/notes/NewConversation.d.ts.map +0 -1
  91. package/dist/components/notes/NewConversation.js +0 -54
  92. package/dist/components/notes/NewConversation.js.map +0 -1
  93. package/dist/donationComponents/DonationPage.d.ts +0 -11
  94. package/dist/donationComponents/DonationPage.d.ts.map +0 -1
  95. package/dist/donationComponents/DonationPage.js +0 -150
  96. package/dist/donationComponents/DonationPage.js.map +0 -1
  97. package/dist/donationComponents/components/BankForm.d.ts +0 -14
  98. package/dist/donationComponents/components/BankForm.d.ts.map +0 -1
  99. package/dist/donationComponents/components/BankForm.js +0 -125
  100. package/dist/donationComponents/components/BankForm.js.map +0 -1
  101. package/dist/donationComponents/components/CardForm.d.ts +0 -13
  102. package/dist/donationComponents/components/CardForm.d.ts.map +0 -1
  103. package/dist/donationComponents/components/CardForm.js +0 -122
  104. package/dist/donationComponents/components/CardForm.js.map +0 -1
  105. package/dist/donationComponents/components/DonationForm.d.ts +0 -15
  106. package/dist/donationComponents/components/DonationForm.d.ts.map +0 -1
  107. package/dist/donationComponents/components/DonationForm.js +0 -200
  108. package/dist/donationComponents/components/DonationForm.js.map +0 -1
  109. package/dist/donationComponents/components/FundDonation.d.ts +0 -12
  110. package/dist/donationComponents/components/FundDonation.d.ts.map +0 -1
  111. package/dist/donationComponents/components/FundDonation.js +0 -32
  112. package/dist/donationComponents/components/FundDonation.js.map +0 -1
  113. package/dist/donationComponents/components/FundDonations.d.ts +0 -11
  114. package/dist/donationComponents/components/FundDonations.d.ts.map +0 -1
  115. package/dist/donationComponents/components/FundDonations.js +0 -33
  116. package/dist/donationComponents/components/FundDonations.js.map +0 -1
  117. package/dist/donationComponents/components/NonAuthDonation.d.ts +0 -12
  118. package/dist/donationComponents/components/NonAuthDonation.d.ts.map +0 -1
  119. package/dist/donationComponents/components/NonAuthDonation.js +0 -27
  120. package/dist/donationComponents/components/NonAuthDonation.js.map +0 -1
  121. package/dist/donationComponents/components/NonAuthDonationInner.d.ts +0 -12
  122. package/dist/donationComponents/components/NonAuthDonationInner.d.ts.map +0 -1
  123. package/dist/donationComponents/components/NonAuthDonationInner.js +0 -276
  124. package/dist/donationComponents/components/NonAuthDonationInner.js.map +0 -1
  125. package/dist/donationComponents/components/PaymentMethods.d.ts +0 -14
  126. package/dist/donationComponents/components/PaymentMethods.d.ts.map +0 -1
  127. package/dist/donationComponents/components/PaymentMethods.js +0 -86
  128. package/dist/donationComponents/components/PaymentMethods.js.map +0 -1
  129. package/dist/donationComponents/components/RecurringDonations.d.ts +0 -10
  130. package/dist/donationComponents/components/RecurringDonations.d.ts.map +0 -1
  131. package/dist/donationComponents/components/RecurringDonations.js +0 -93
  132. package/dist/donationComponents/components/RecurringDonations.js.map +0 -1
  133. package/dist/donationComponents/components/RecurringDonationsEdit.d.ts +0 -11
  134. package/dist/donationComponents/components/RecurringDonationsEdit.d.ts.map +0 -1
  135. package/dist/donationComponents/components/RecurringDonationsEdit.js +0 -66
  136. package/dist/donationComponents/components/RecurringDonationsEdit.js.map +0 -1
  137. package/dist/donationComponents/components/index.d.ts +0 -10
  138. package/dist/donationComponents/components/index.d.ts.map +0 -1
  139. package/dist/donationComponents/components/index.js +0 -22
  140. package/dist/donationComponents/components/index.js.map +0 -1
  141. package/dist/donationComponents/index.d.ts +0 -4
  142. package/dist/donationComponents/index.d.ts.map +0 -1
  143. package/dist/donationComponents/index.js +0 -10
  144. package/dist/donationComponents/index.js.map +0 -1
  145. package/dist/donationComponents/modals/DonationPreviewModal.d.ts +0 -15
  146. package/dist/donationComponents/modals/DonationPreviewModal.d.ts.map +0 -1
  147. package/dist/donationComponents/modals/DonationPreviewModal.js +0 -33
  148. package/dist/donationComponents/modals/DonationPreviewModal.js.map +0 -1
  149. package/src/components/B1ShareModal.tsx +0 -104
  150. package/src/components/CreatePerson.tsx +0 -135
  151. package/src/components/PersonAdd.tsx +0 -81
  152. package/src/components/gallery/GalleryModal.tsx +0 -139
  153. package/src/components/gallery/StockPhotos.tsx +0 -75
  154. package/src/components/gallery/index.ts +0 -1
  155. package/src/components/iconPicker/IconNamesList.ts +0 -2240
  156. package/src/components/iconPicker/IconPicker.tsx +0 -160
  157. package/src/components/notes/Conversation.tsx +0 -84
  158. package/src/components/notes/Conversations.tsx +0 -60
  159. package/src/components/notes/NewConversation.tsx +0 -80
  160. package/src/donationComponents/DonationPage.tsx +0 -215
  161. package/src/donationComponents/components/BankForm.tsx +0 -161
  162. package/src/donationComponents/components/CardForm.tsx +0 -106
  163. package/src/donationComponents/components/DonationForm.tsx +0 -255
  164. package/src/donationComponents/components/FundDonation.tsx +0 -58
  165. package/src/donationComponents/components/FundDonations.tsx +0 -44
  166. package/src/donationComponents/components/NonAuthDonation.tsx +0 -33
  167. package/src/donationComponents/components/NonAuthDonationInner.tsx +0 -295
  168. package/src/donationComponents/components/PaymentMethods.tsx +0 -137
  169. package/src/donationComponents/components/RecurringDonations.tsx +0 -123
  170. package/src/donationComponents/components/RecurringDonationsEdit.tsx +0 -95
  171. package/src/donationComponents/components/index.tsx +0 -9
  172. package/src/donationComponents/index.ts +0 -3
  173. package/src/donationComponents/modals/DonationPreviewModal.tsx +0 -68
  174. /package/dist/components/{iconPicker → markdownEditor}/IconNamesList.d.ts +0 -0
@@ -1,160 +0,0 @@
1
- "use client";
2
-
3
- import * as React from "react";
4
- import { styled, Icon, InputBase, Typography, debounce, Grid, IconButton, Pagination, Stack } from "@mui/material";
5
- import MuiPaper from "@mui/material/Paper";
6
- import IconNamesList from "./IconNamesList"
7
- import { Locale } from "../../helpers";
8
- import FlexSearch from "flexsearch";
9
- const UPDATE_SEARCH_INDEX_WAIT_MS = 220;
10
-
11
- //const FlexSearch = require("flexsearch");
12
-
13
- const StyledIconSpan = styled("span")(({ theme }) => ({
14
- display: "inline-flex",
15
- flexDirection: "column",
16
- color: theme.palette.text.secondary,
17
- margin: "0 4px",
18
- "& > div": {
19
- display: "flex"
20
- },
21
- "& > div > *": {
22
- flexGrow: 1,
23
- fontSize: ".6rem",
24
- overflow: "hidden",
25
- textOverflow: "ellipsis",
26
- textAlign: "center",
27
- width: 0
28
- }
29
- }));
30
-
31
- const StyledIcon: any = styled(Icon)(({ theme }) => ({
32
- boxSizing: "content-box",
33
- cursor: "pointer",
34
- color: theme.palette.text.primary,
35
- borderRadius: theme.shape.borderRadius,
36
- transition: theme.transitions.create(["background-color", "box-shadow"], {
37
- duration: theme.transitions.duration.shortest
38
- }),
39
- padding: theme.spacing(1),
40
- margin: theme.spacing(0.5, 0),
41
- "&:hover": {
42
- backgroundColor: theme.palette.background.paper,
43
- boxShadow: theme.shadows[1]
44
- }
45
- }));
46
-
47
- const Icons = React.memo(function Icons(props: { icons: string[]; handleOpenClick: (name: string) => void; }) {
48
- const { icons, handleOpenClick } = props;
49
-
50
- const handleIconClick = (name: string) => {
51
- const camel = name.substring(0, 1).toLocaleLowerCase() + name.substring(1, name.length);
52
- const underscored = camel.replace(/[A-Z]/g, m => "_" + m.toLowerCase());
53
- handleOpenClick(underscored)
54
- }
55
-
56
- return (
57
- <div>
58
- {icons.map((icon) => (
59
- <StyledIconSpan key={icon}>
60
- <StyledIcon tabIndex={-1} sx={{ fontSize: "34px !important" }} onClick={() => { handleIconClick(icon) }} aria-label={`Select ${icon} icon`} data-testid={`icon-${icon}`}>
61
- {icon}
62
- </StyledIcon>
63
- </StyledIconSpan>
64
- ))}
65
- </div>
66
- );
67
- });
68
-
69
- const Paper = styled(MuiPaper)(({ theme }) => ({ padding: "2px 4px", display: "flex", alignItems: "center", marginBottom: theme.spacing(2), width: "100%" }));
70
-
71
- const Input = styled(InputBase)({ marginLeft: 8, flex: 1 });
72
-
73
- const searchIndex = new FlexSearch.Index({ tokenize: "full" });
74
- //const searchIndex = FlexSearch.create({ tokenize: "full" });
75
-
76
- function createSearchIndex() {
77
- // create component names from icons list
78
- const iconsAndComponentNames = IconNamesList.map(icon => {
79
- const split = icon.split("_");
80
- const capitalizedSplit = split.map(s => {
81
- if (isAlphabet(s[0])) {
82
- s = s[0].toUpperCase() + s.slice(1)
83
- }
84
- return s
85
- })
86
- return {
87
- iconName: icon,
88
- componentName: capitalizedSplit.join("")
89
- };
90
- })
91
-
92
- // create search index
93
- iconsAndComponentNames.forEach(icon => {
94
- let searchTerm = icon.iconName + " " + icon.componentName;
95
-
96
- searchIndex.addAsync(icon.iconName, searchTerm)
97
- //searchIndex.add(searchTerm);
98
- })
99
-
100
- }
101
-
102
- function isAlphabet(character: string) {
103
- return /[a-zA-Z]/.test(character);
104
- }
105
-
106
- createSearchIndex();
107
-
108
- type Props = {
109
- onSelect: (iconName: string) => void;
110
- };
111
-
112
- const defaultIcons = ["person", "group", "groups", "contact_mail", "mail", "church", "favorite", "volunteer_activism", "link", "home", "apps", "web", "public", "rss_feed", "videocam", "live_tv", "music_note", "menu_book", "star", "accessible", "woman", "man", "child_care", "handshake", "location_on", "restaurant", "local_cafe" ];
113
-
114
- export function IconPicker(props: Props) {
115
- const pageSize = 27;
116
- const [keys, setKeys] = React.useState<any[] | null>(null);
117
- const [query, setQuery] = React.useState("");
118
- const [page, setPage] = React.useState(1);
119
-
120
- const updateSearchResults = React.useMemo(
121
- () =>
122
- debounce((value) => {
123
- if (value === "") setKeys(defaultIcons);
124
- else searchIndex.searchAsync(value, { limit: 3000 }).then((results: any) => { setKeys(results); setPage(1); });
125
- //else searchIndex.search(value, { limit: 3000 }).then((results: any) => { setKeys(results); setPage(1); });
126
- }, UPDATE_SEARCH_INDEX_WAIT_MS),
127
- []
128
- );
129
-
130
- React.useEffect(() => { updateSearchResults(query); return () => { updateSearchResults.clear(); }; }, [query, updateSearchResults]);
131
-
132
- function paged<T>(array: Array<T>, p: number) {
133
- return array.slice((p - 1) * pageSize, p * pageSize);
134
- }
135
-
136
- const icons = keys || IconNamesList;
137
-
138
- const pagesCount = Math.ceil(icons.length / pageSize);
139
-
140
- return (
141
- <Grid container sx={{ minHeight: 360, padding: "16px" }}>
142
- <Grid size={12}>
143
- <Paper>
144
- <IconButton sx={{ padding: "10px" }} aria-label="search" data-testid="icon-search-button">
145
- <Icon>search</Icon>
146
- </IconButton>
147
- <Input autoFocus value={query} onChange={(event) => setQuery(event.target.value)} placeholder={Locale.label("iconPicker.searchIcons")} inputProps={{ "aria-label": "search icons" }} data-testid="icon-search-input" />
148
- </Paper>
149
- {(query === "") && <Typography sx={{ mb: 1 }}>{IconNamesList.length} {Locale.label("iconPicker.iconsAvailable")}</Typography>}
150
- {(query !== "") && <Typography sx={{ mb: 1 }}>{icons.length} {Locale.label("iconPicker.matchingResults")}</Typography>}
151
-
152
- <Icons icons={paged(icons, page)} handleOpenClick={props.onSelect} />
153
- </Grid>
154
-
155
- <Stack spacing={2} sx={{ margin: "0 auto" }}>
156
- <Pagination count={pagesCount} page={page} onChange={(_, p) => setPage(p)} size="small" />
157
- </Stack>
158
- </Grid>
159
- );
160
- }
@@ -1,84 +0,0 @@
1
- "use client";
2
-
3
- import { Box, Paper, Stack } from "@mui/material";
4
- import React from "react";
5
- import { ApiHelper, ArrayHelper, DateHelper, Locale, PersonHelper } from "../../helpers";
6
- import { ConversationInterface, MessageInterface, UserContextInterface } from "@churchapps/helpers";
7
- import { AddNote } from "./AddNote";
8
- import { Note } from "./Note";
9
-
10
- interface Props {
11
- conversation: ConversationInterface;
12
- context: UserContextInterface;
13
- showCommentCount?: boolean;
14
- noWrapper?: boolean;
15
- }
16
-
17
- export function Conversation(props: Props) {
18
- const [conversation, setConversation] = React.useState<ConversationInterface>(null)
19
- const [editMessageId, setEditMessageId] = React.useState(null)
20
-
21
- const loadNotes = async () => {
22
- const messages: MessageInterface[] = (conversation.id) ? await ApiHelper.get("/messages/conversation/" + conversation.id, "MessagingApi") : [];
23
- if (messages.length > 0) {
24
- const peopleIds = ArrayHelper.getIds(messages, "personId");
25
- const people = await ApiHelper.get("/people/ids?ids=" + peopleIds.join(","), "MembershipApi");
26
- messages.forEach(n => {
27
- n.person = ArrayHelper.getOne(people, "id", n.personId);
28
- })
29
- }
30
- const c = { ...conversation }
31
- c.messages = messages;
32
- setConversation(c);
33
- setEditMessageId(null);
34
- };
35
-
36
- React.useEffect(() => { setConversation(props.conversation) }, [props.conversation]); //eslint-disable-line
37
-
38
- if (conversation === null) return null;
39
- else {
40
- const message = conversation.messages[0];
41
- const photoUrl = PersonHelper.getPhotoUrl(message?.person);
42
- let datePosted = new Date(message.timeUpdated || message.timeSent);
43
- const displayDuration = DateHelper.getDisplayDuration(datePosted);
44
-
45
- const isEdited = message.timeUpdated && message.timeUpdated !== message.timeSent && <>(edited)</>;
46
- const contents = message.content?.split("\n");
47
-
48
- const getNotes = () => {
49
- let noteArray: React.ReactNode[] = [];
50
- for (let i = 1; i < conversation.messages.length; i++) noteArray.push(<Note context={props.context} message={conversation.messages[i]} key={conversation.messages[i].id} showEditNote={setEditMessageId} />);
51
- return noteArray;
52
- }
53
-
54
- let result = (<>
55
- <div className="conversation">
56
- <div className="postedBy">
57
- <img src={photoUrl} alt="avatar" />
58
- </div>
59
- <Box sx={{ width: "100%" }} className="conversationContents">
60
- <Stack direction="row" justifyContent="space-between">
61
- <div>
62
- <b>{message.person?.name?.display}</b> · <span className="text-grey">{displayDuration}{isEdited}</span>
63
- {contents.map((c, i) => c ? <p key={i}>{c}</p> : <br />)}
64
- </div>
65
- </Stack>
66
- </Box>
67
- </div>
68
- {props.showCommentCount && (
69
- <div className="commentCount">
70
- <div>{(conversation.postCount === 1) ? "1 " + Locale.label("notes.comment") : conversation.postCount + " " + Locale.label("notes.comments")}</div>
71
- {(conversation.postCount > conversation.messages.length) ? <a href="about:blank" onClick={(e) => { e.preventDefault(); loadNotes(); }}>{Locale.label("notes.viewAll")} {conversation.postCount} {Locale.label("notes.comments")}</a> : <>&nbsp;</>}
72
- </div>
73
- )}
74
- <div className="messages">
75
- {getNotes()}
76
- <AddNote context={props.context} conversationId={props.conversation.id} onUpdate={loadNotes} createConversation={async () => (props.conversation.id)} messageId={editMessageId} />
77
- </div>
78
- </>);
79
-
80
-
81
- if (props.noWrapper) return result;
82
- else return (<Paper sx={{ padding: 1, marginBottom: 2 }}>{result}</Paper>);
83
- }
84
- };
@@ -1,60 +0,0 @@
1
- "use client";
2
-
3
- import React from "react";
4
- import { Loading } from "../";
5
- import { ApiHelper, ArrayHelper } from "../../helpers";
6
- import { ConversationInterface, UserContextInterface } from "@churchapps/helpers";
7
- import { Conversation } from "./Conversation";
8
- import { NewConversation } from "./NewConversation";
9
-
10
- interface Props {
11
- contentType: string;
12
- contentId: string;
13
- groupId: string;
14
- context: UserContextInterface;
15
- }
16
-
17
- export function Conversations(props: Props) {
18
-
19
- const [conversations, setConversations] = React.useState<ConversationInterface[]>(null)
20
- //const [editMessageId, setEditMessageId] = React.useState(null)
21
-
22
- const loadConversations = async () => {
23
- const conversations: ConversationInterface[] = (props.contentId) ? await ApiHelper.get("/conversations/" + props.contentType + "/" + props.contentId, "MessagingApi") : [];
24
- if (conversations.length > 0) {
25
- const peopleIds: string[] = [];
26
- conversations.forEach(c => {
27
- c.messages.forEach(m => {
28
- if (peopleIds.indexOf(m.personId) === -1) peopleIds.push(m.personId);
29
- });
30
- })
31
- const people = await ApiHelper.get("/people/ids?ids=" + peopleIds.join(","), "MembershipApi");
32
- conversations.forEach(c => {
33
- c.messages.forEach(m => {
34
- m.person = ArrayHelper.getOne(people, "id", m.personId);
35
- });
36
- })
37
- }
38
- setConversations(conversations);
39
- //setEditMessageId(null);
40
- };
41
-
42
- const getConversations = () => {
43
- if (conversations.length === 0) return <></>
44
- else {
45
- let noteArray: React.ReactNode[] = [];
46
- for (let i = 0; i < conversations.length; i++) noteArray.push(<Conversation context={props.context} conversation={conversations[i]} key={conversations[i].id} />);
47
- return noteArray;
48
- }
49
- }
50
-
51
- React.useEffect(() => { loadConversations() }, [props.contentId]); //eslint-disable-line
52
-
53
- if (!conversations) return <Loading />
54
- else return (
55
- <>
56
- <NewConversation context={props.context} contentType={props.contentType} contentId={props.contentId} onUpdate={loadConversations} groupId={props.groupId} visibility="public" />
57
- {getConversations()}
58
- </>
59
- );
60
- };
@@ -1,80 +0,0 @@
1
- "use client";
2
-
3
- import { Icon, Paper, Stack, TextField } from "@mui/material";
4
- import React from "react";
5
- import { ApiHelper, Locale, PersonHelper } from "../../helpers";
6
- import { ConversationInterface, MessageInterface, UserContextInterface } from "@churchapps/helpers";
7
- import { ErrorMessages } from "../ErrorMessages";
8
- import { SmallButton } from "../SmallButton";
9
-
10
- interface Props {
11
- contentType: string;
12
- contentId: string;
13
- groupId: string;
14
- visibility: "public" | "hidden";
15
- context: UserContextInterface;
16
- onUpdate: () => void;
17
- }
18
-
19
- export function NewConversation({ context, ...props }: Props) {
20
- const [message, setMessage] = React.useState<MessageInterface>({})
21
- const [errors, setErrors] = React.useState<string[]>([]);
22
- const [isSubmitting, setIsSubmitting] = React.useState(false);
23
-
24
- const handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement>) => {
25
- setErrors([]);
26
- const m = { ...message } as MessageInterface;
27
- m.content = e.target.value;
28
- setMessage(m);
29
- }
30
-
31
- const validate = () => {
32
- const result = [];
33
- if (!message.content.trim()) result.push(Locale.label("notes.validate.content"));
34
- setErrors(result);
35
- return result.length === 0;
36
- }
37
-
38
- async function handleSave() {
39
- if (validate()) {
40
- setIsSubmitting(true);
41
-
42
- const conv: ConversationInterface = { contentType: props.contentType, contentId: props.contentId, title: props.contentType + " " + props.contentId + " Conversation", groupId: props.groupId, visibility: props.visibility, allowAnonymousPosts: false }
43
- const result = await ApiHelper.post("/conversations", [conv], "MessagingApi");
44
- const cId = result[0].id;
45
-
46
- const m = { ...message };
47
- m.conversationId = cId;
48
- ApiHelper.post("/messages", [m], "MessagingApi")
49
- .then(() => {
50
- props.onUpdate();
51
- const m = { ...message } as MessageInterface;
52
- m.content = "";
53
- setMessage(m);
54
- })
55
- .finally(() => { setIsSubmitting(false); });
56
-
57
- }
58
- };
59
-
60
- const image = PersonHelper.getPhotoUrl(context?.person)
61
-
62
- return (
63
- <Paper sx={{ padding: 1, marginBottom: 2 }}>
64
- <ErrorMessages errors={errors} />
65
-
66
- <Stack direction="row" spacing={1.5} style={{ marginTop: 15 }} justifyContent="end">
67
-
68
- {image ? <img src={image} alt="user" style={{ width: 60, height: 45, borderRadius: 5, marginLeft: 8 }} /> : <Icon>person</Icon>}
69
- <Stack direction="column" spacing={2} style={{ width: "100%" }} justifyContent="end">
70
- <div><b>{context?.person?.name?.display}</b></div>
71
- <TextField fullWidth name="noteText" aria-label={Locale.label("notes.startConversation")} placeholder={Locale.label("notes.startConversation")} multiline style={{ marginTop: 0, border: "none" }} variant="standard" onChange={handleChange} value={message.content} />
72
- </Stack>
73
- <Stack direction="column" spacing={1} justifyContent="end">
74
- <SmallButton icon="send" onClick={handleSave} disabled={isSubmitting} />
75
- </Stack>
76
- </Stack>
77
- </Paper>
78
- );
79
-
80
- };
@@ -1,215 +0,0 @@
1
- "use client";
2
-
3
- import React from "react";
4
- import { loadStripe, Stripe } from "@stripe/stripe-js";
5
- import { DonationForm, RecurringDonations, PaymentMethods } from "./components";
6
- import { DisplayBox, ExportLink, Loading } from "../components";
7
- import { ApiHelper, DateHelper, UniqueIdHelper, CurrencyHelper, Locale } from "../helpers";
8
- import { DonationInterface, PersonInterface, StripePaymentMethod, ChurchInterface } from "@churchapps/helpers";
9
- // import { Link } from "react-router-dom"
10
- import { Table, TableBody, TableRow, TableCell, TableHead, Alert, Button, Icon, Link, Menu, MenuItem } from "@mui/material"
11
- import { useMountedState } from "../hooks/useMountedState";
12
-
13
- interface Props { personId: string, appName?: string, church?: ChurchInterface, churchLogo?: string }
14
-
15
- export const DonationPage: React.FC<Props> = (props) => {
16
- const [donations, setDonations] = React.useState<DonationInterface[]>(null);
17
- const [stripePromise, setStripe] = React.useState<Promise<Stripe>>(null);
18
- const [paymentMethods, setPaymentMethods] = React.useState<StripePaymentMethod[]>(null);
19
- const [customerId, setCustomerId] = React.useState(null);
20
- const [person, setPerson] = React.useState<PersonInterface>(null);
21
- const [message, setMessage] = React.useState<string>(null);
22
- const [appName, setAppName] = React.useState<string>("");
23
- const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
24
- const open = Boolean(anchorEl);
25
- const isMounted = useMountedState();
26
-
27
- const handleClose = () => {
28
- setAnchorEl(null);
29
- }
30
-
31
- const loadPaymentMethods = async () => {
32
- try {
33
- const data = await ApiHelper.get("/paymentmethods/personid/" + props.personId, "GivingApi");
34
- if (!data.length) {
35
- setPaymentMethods([]);
36
- return;
37
- }
38
-
39
- const cards = data[0].cards.data.map((card: any) => new StripePaymentMethod(card));
40
- const banks = data[0].banks.data.map((bank: any) => new StripePaymentMethod(bank));
41
- setCustomerId(data[0].customer.id);
42
- setPaymentMethods(cards.concat(banks));
43
- } catch (error) {
44
- console.error("Error loading payment methods:", error);
45
- setPaymentMethods([]);
46
- }
47
- }
48
-
49
- const loadPersonData = async () => {
50
- try {
51
- const data = await ApiHelper.get("/people/" + props.personId, "MembershipApi");
52
- setPerson(data);
53
- } catch (error) {
54
- console.error("Error loading person data:", error);
55
- }
56
- }
57
-
58
- const loadStripeData = async (gatewayData: any) => {
59
- if (!gatewayData.length || !gatewayData[0]?.publicKey) {
60
- setPaymentMethods([]);
61
- return;
62
- }
63
-
64
- setStripe(loadStripe(gatewayData[0].publicKey));
65
- await Promise.all([
66
- // loadPersonData(),
67
- loadPaymentMethods()
68
- ]);
69
- }
70
-
71
- const loadData = async () => {
72
- if (props?.appName) setAppName(props.appName);
73
- if (UniqueIdHelper.isMissing(props.personId)) return;
74
-
75
- try {
76
- const [donationsData, gatewaysData] = await Promise.all([
77
- ApiHelper.get("/donations?personId=" + props.personId, "GivingApi"),
78
- ApiHelper.get("/gateways", "GivingApi")
79
- ]);
80
-
81
- setDonations(donationsData);
82
- await loadPersonData(); //moved this outside of loadStripeData to fix issue with person data not loading when there's no gateway data
83
- await loadStripeData(gatewaysData);
84
- } catch (error) {
85
- console.error("Error loading donation data:", error);
86
- setDonations([]);
87
- setPaymentMethods([]);
88
- }
89
- }
90
-
91
- const handleDataUpdate = (message?: string) => {
92
- setMessage(message)
93
- setPaymentMethods(null);
94
- loadData();
95
- }
96
-
97
- const getEditContent = () => {
98
- const result: React.ReactElement[] = [];
99
- const date = new Date();
100
- const currentY = date.getFullYear();
101
- const lastY = date.getFullYear() - 1;
102
-
103
- const current_year = (donations.length>0) ? donations.filter(d => new Date(d.donationDate || "2000-01-01").getFullYear() === currentY) : [];
104
- const last_year = (donations.length>0) ? donations.filter(d => new Date(d.donationDate || "2000-01-01").getFullYear() === lastY) : [];
105
- const customHeaders = [
106
- { label: "amount", key: "amount" },
107
- { label: "donationDate", key: "donationDate" },
108
- { label: "fundName", key: "fund.name" },
109
- { label: "method", key: "method"},
110
- { label: "methodDetails", key: "methodDetails"},
111
- ]
112
-
113
- result.push(<>
114
- <Button
115
- id="download-button"
116
- aria-controls={open ? "download-menu" : undefined}
117
- aria-haspopup="true"
118
- aria-expanded={open ? "true" : undefined}
119
- onClick={(e: React.MouseEvent<HTMLButtonElement>) => {
120
- setAnchorEl(e.currentTarget);
121
- }}
122
- >
123
- <Icon>download</Icon>
124
- </Button>
125
- <Menu
126
- id="download-menu"
127
- anchorEl={anchorEl}
128
- open={open}
129
- onClose={handleClose}
130
- MenuListProps={{ 'aria-labelledby': "download-button" }}
131
- >
132
- <MenuItem onClick={handleClose} dense><ExportLink data={current_year} filename="current_year_donations" customHeaders={customHeaders} text="Current Year (CSV)" icon="table_chart" /></MenuItem>
133
- <MenuItem onClick={handleClose} dense><Link href={"/donations/print/" + person?.id}><Button><Icon>print</Icon> &nbsp; Current Year (PRINT)</Button></Link></MenuItem>
134
- <MenuItem onClick={handleClose} dense><ExportLink data={last_year} filename="last_year_donations" customHeaders={customHeaders} text="Last Year (CSV)" icon="table_chart" /></MenuItem>
135
- <MenuItem onClick={handleClose} dense><Link href={"/donations/print/" + person?.id + "?prev=1"}><Button><Icon>print</Icon> &nbsp; Last Year (PRINT)</Button></Link></MenuItem>
136
- </Menu>
137
- </>);
138
-
139
- return result;
140
- }
141
-
142
- const getRows = () => {
143
- let rows: React.ReactElement[] = [];
144
-
145
- if (donations.length === 0) {
146
- rows.push(<TableRow key="0"><TableCell>{Locale.label("donation.page.willAppear")}</TableCell></TableRow>);
147
- return rows;
148
- }
149
-
150
- for (let i = 0; i < donations.length; i++) {
151
- let d = donations[i];
152
- rows.push(
153
- <TableRow key={i}>
154
- {appName !== "B1App" && <TableCell><Link href={"/donations/" + d.batchId}>{d.batchId}</Link></TableCell>}
155
- <TableCell>{DateHelper.prettyDate(new Date(d.donationDate))}</TableCell>
156
- <TableCell>{d.method} - {d.methodDetails}</TableCell>
157
- <TableCell>{d.fund.name}</TableCell>
158
- <TableCell>{CurrencyHelper.formatCurrency(d.fund.amount)}</TableCell>
159
- </TableRow>
160
- );
161
- }
162
- return rows;
163
- }
164
-
165
- const getTableHeader = () => {
166
- const rows: React.ReactElement[] = []
167
-
168
- if (donations.length > 0) {
169
- rows.push(
170
- <TableRow key="header" sx={{textAlign: "left"}}>
171
- {appName !== "B1App" && <th>{Locale.label("donation.page.batch")}</th>}
172
- <th>{Locale.label("donation.page.date")}</th>
173
- <th>{Locale.label("donation.page.method")}</th>
174
- <th>{Locale.label("donation.page.fund")}</th>
175
- <th>{Locale.label("donation.page.amount")}</th>
176
- </TableRow>
177
- );
178
- }
179
-
180
- return rows;
181
- }
182
-
183
- React.useEffect(() => {
184
- loadData();
185
- }, [props.personId]); //eslint-disable-line
186
-
187
- const getTable = () => {
188
- if (!donations) return <Loading />;
189
- else return (<Table>
190
- <TableHead>{getTableHeader()}</TableHead>
191
- <TableBody>{getRows()}</TableBody>
192
- </Table>);
193
- }
194
-
195
- const getPaymentMethodComponents = () => {
196
- if (!paymentMethods || !donations) return <Loading />;
197
- else return (
198
- <>
199
- <DonationForm person={person} customerId={customerId} paymentMethods={paymentMethods} stripePromise={stripePromise} donationSuccess={handleDataUpdate} church={props?.church} churchLogo={props?.churchLogo} />
200
- <DisplayBox headerIcon="payments" headerText="Donations" editContent={getEditContent()}>
201
- {getTable()}
202
- </DisplayBox>
203
- <RecurringDonations customerId={customerId} paymentMethods={paymentMethods} appName={appName} dataUpdate={handleDataUpdate} />
204
- <PaymentMethods person={person} customerId={customerId} paymentMethods={paymentMethods} appName={appName} stripePromise={stripePromise} dataUpdate={handleDataUpdate} />
205
- </>
206
- );
207
- }
208
-
209
- return (
210
- <>
211
- {paymentMethods && message && <Alert severity="success">{message}</Alert>}
212
- {getPaymentMethodComponents()}
213
- </>
214
- );
215
- }