@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,170 +0,0 @@
1
- "use client";
2
-
3
- import { FileHelper } from "../../helpers/FileHelper";
4
- import { ApiHelper } from "../../helpers";
5
- import { Locale } from "../../helpers";
6
- import { CommonEnvironmentHelper } from "@churchapps/helpers";
7
- import { Box, Button, Dialog, DialogActions, DialogContent, DialogTitle, FormControl, Grid, IconButton, InputLabel, MenuItem, Select, Tab, Tabs, Tooltip, Icon } from "@mui/material";
8
- import React, { useState } from "react";
9
- import { ImageEditor } from "../ImageEditor";
10
- import { TabPanel } from "../TabPanel";
11
- import { StockPhotos } from "./StockPhotos";
12
-
13
- interface Props {
14
- aspectRatio: number,
15
- onClose: () => void,
16
- onSelect: (img: string) => void
17
- }
18
-
19
- export const GalleryModal: React.FC<Props> = (props: Props) => {
20
- const [images, setImages] = useState<string[]>([]);
21
- const [tabIndex, setTabIndex] = React.useState(0);
22
- const [aspectRatio, setAspectRatio] = React.useState(Math.round(props.aspectRatio * 100) / 100);
23
- const [editorPhotoUrl, setEditorPhotoUrl] = React.useState("");
24
-
25
- const contentRoot = CommonEnvironmentHelper.ContentRoot;
26
-
27
- const handleTabChange = (el: any, newValue: any) => { setTabIndex(newValue); }
28
-
29
- const loadData = () => { ApiHelper.get("/gallery/" + aspectRatio.toString(), "ContentApi").then((data: any) => setImages(data.images)); }
30
-
31
- const handleImageUpdated = async (dataUrl: string) => {
32
-
33
- if (!dataUrl) {
34
- console.warn('No dataUrl provided to handleImageUpdated');
35
- return;
36
- }
37
-
38
- try {
39
- const fileName = Math.floor(Date.now() / 1000).toString() + ".jpg"
40
- const blob = FileHelper.dataURLtoBlob(dataUrl);
41
- const file = new File([blob], "file_name");
42
-
43
- const params = { folder: aspectRatio.toString(), fileName };
44
-
45
- const presigned = await ApiHelper.post("/gallery/requestUpload", params, "ContentApi");
46
- const doUpload = presigned.key !== undefined;
47
-
48
- if (doUpload) {
49
- await FileHelper.postPresignedFile(presigned, file, () => { });
50
- } else {
51
- console.warn('Upload failed - no presigned key received');
52
- }
53
-
54
- setTabIndex(0);
55
- loadData();
56
- } catch (error) {
57
- console.error('Error in handleImageUpdated:', error);
58
- // In case of API failure, still provide feedback to user
59
- alert('Image processing completed, but upload failed. API may not be available in this environment.');
60
- }
61
- };
62
-
63
- const handleDelete = (folder: string, image: string) => {
64
- if (window.confirm(Locale.label("gallery.confirmDelete"))) {
65
- ApiHelper.delete("/gallery/" + folder + "/" + image, "ContentApi").then(() => { loadData(); });
66
- }
67
- }
68
-
69
- React.useEffect(() => { if (aspectRatio !== props.aspectRatio) setAspectRatio(Math.round(props.aspectRatio * 100) / 100) }, [props.aspectRatio]); //eslint-disable-line
70
- React.useEffect(loadData, [aspectRatio]); //eslint-disable-line
71
-
72
- const getImages = () => {
73
- let result: React.ReactElement[] = [];
74
- images.forEach((img: any, index: number) => {
75
- const parts = img.split("/");
76
-
77
- result.push(<Grid key={img || index} size={{ xs: 12, md: 4 }}>
78
- <Box sx={{ position: "relative", ":hover #deleteIcon": { visibility: "visible" } }}>
79
- <a href="about:blank" onClick={(e) => { e.preventDefault(); props.onSelect(contentRoot + "/" + img) }} aria-label="Select image" data-testid="select-image">
80
- <Box
81
- component="img"
82
- src={contentRoot + "/" + img}
83
- alt="custom"
84
- sx={{
85
- width: '100%',
86
- height: 'auto',
87
- maxWidth: '100%',
88
- display: 'block'
89
- }}
90
- />
91
- </a>
92
- <Box id="deleteIcon" sx={{ position: "absolute", top: 3, right: 3, visibility: "hidden", backgroundColor: "whitesmoke", borderRadius: 5 }}>
93
- <Tooltip title="Delete">
94
- <IconButton size="small" color="error" onClick={() => handleDelete(parts[2], parts[3])} aria-label="Delete image" data-testid="delete-image">
95
- <Icon sx={{ fontSize: "17px !important" }}>delete_outline</Icon>
96
- </IconButton>
97
- </Tooltip>
98
- </Box>
99
- </Box>
100
- </Grid>);
101
- })
102
- return result;
103
- }
104
-
105
- const handleStockSelect = (url: string) => {
106
- setEditorPhotoUrl(url);
107
- setTabIndex(1);
108
- }
109
-
110
- const getDisplayAspect = () => {
111
- let result = aspectRatio.toString();
112
- if (aspectRatio === 0) result = "Free Form";
113
- else if (aspectRatio === 1) result = "1:1";
114
- else if (aspectRatio === 2) result = "2:1";
115
- else if (aspectRatio === 3) result = "3:1";
116
- else if (aspectRatio === 4) result = "4:1";
117
- else if (aspectRatio === 1.33) result = "4:3";
118
- else if (aspectRatio === 1.78) result = "16:9";
119
- else if (aspectRatio === 0.5) result = "1:2";
120
- else if (aspectRatio === 0.5625) result = "9:16";
121
- return result;
122
- }
123
-
124
- return (<>
125
- <Dialog open={true} onClose={props.onClose}>
126
- <DialogTitle>Select a Photo</DialogTitle>
127
- <DialogContent style={{ overflowX: "hidden" }}>
128
-
129
- {(props.aspectRatio === 0) && (
130
- <FormControl fullWidth>
131
- <InputLabel>{Locale.label("gallery.aspectRatio")}</InputLabel>
132
- <Select size="small" label={Locale.label("gallery.aspectRatio")} name="aspectRatio" value={aspectRatio} onChange={(e) => setAspectRatio(parseFloat(e.target.value.toString()))}>
133
- <MenuItem value="0">{Locale.label("gallery.freeForm")}</MenuItem>
134
- <MenuItem value="1">1:1</MenuItem>
135
- <MenuItem value="2">2:1</MenuItem>
136
- <MenuItem value="3">3:1</MenuItem>
137
- <MenuItem value="4">4:1</MenuItem>
138
- <MenuItem value="1.33">4:3</MenuItem>
139
- <MenuItem value="1.78">16:9</MenuItem>
140
- <MenuItem value="0.5">1:2</MenuItem>
141
- <MenuItem value="0.5625">9:16</MenuItem>
142
- </Select>
143
- </FormControl>
144
- )}
145
-
146
- <Tabs variant="fullWidth" value={tabIndex} onChange={handleTabChange}>
147
- <Tab label="Gallery" />
148
- <Tab label="Upload" />
149
- <Tab label="Stock Photos" />
150
- </Tabs>
151
- <TabPanel value={tabIndex} index={0}>
152
-
153
- <Grid container spacing={3} alignItems="center">
154
- {getImages()}
155
- </Grid>
156
- </TabPanel>
157
- <TabPanel value={tabIndex} index={1}>
158
- <div>{Locale.label("gallery.aspectRatio")}: {getDisplayAspect()}</div>
159
- <ImageEditor onUpdate={handleImageUpdated} photoUrl={editorPhotoUrl} aspectRatio={aspectRatio} outputWidth={1280} outputHeight={768} hideDelete={true} />
160
- </TabPanel>
161
- <TabPanel value={tabIndex} index={2}>
162
- <StockPhotos aspectRatio={aspectRatio} onSelect={props.onSelect} onStockSelect={handleStockSelect} />
163
- </TabPanel>
164
- </DialogContent>
165
- <DialogActions sx={{ paddingX: "16px", paddingBottom: "12px" }}>
166
- <Button variant="outlined" onClick={props.onClose}>Close</Button>
167
- </DialogActions>
168
- </Dialog>
169
- </>);
170
- };
@@ -1,96 +0,0 @@
1
- "use client";
2
- import { Button, Grid, TextField } from "@mui/material";
3
- import React, { useState } from "react";
4
- import { ApiHelper } from "../../helpers";
5
- import { Locale } from "../../helpers";
6
- import { CommonEnvironmentHelper } from "@churchapps/helpers";
7
-
8
- interface Props {
9
- aspectRatio: number,
10
- onSelect: (img: string) => void,
11
- onStockSelect: (img: string) => void
12
- }
13
-
14
- interface SearchResult { description: string, url: string, photographer: string, photographerUrl: string, large: string, thumbnail: string }
15
-
16
- export const StockPhotos: React.FC<Props> = (props: Props) => {
17
- const [images, setImages] = useState<string[]>([]);
18
- const [searchText, setSearchText] = useState("");
19
- const [searchResults, setSearchResults] = useState<SearchResult[]>(null);
20
-
21
- const contentRoot = CommonEnvironmentHelper.ContentRoot;
22
-
23
- const loadData = () => { ApiHelper.getAnonymous("/gallery/stock/" + props.aspectRatio.toString(), "ContentApi").then((data: any) => setImages(data.images)); }
24
-
25
- React.useEffect(loadData, [props.aspectRatio]); //eslint-disable-line
26
-
27
- const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => { e.preventDefault(); setSearchText(e.currentTarget.value); }
28
-
29
- const handleSearch = (e: React.MouseEvent) => {
30
- if (e !== null) e.preventDefault();
31
- let term = searchText.trim();
32
- ApiHelper.post("/stock/search", { term: term }, "ContentApi").then((data: SearchResult[]) => { setSearchResults(data) });
33
- }
34
-
35
- const getImages = () => {
36
- if (searchResults) return getResults();
37
- else return getSuggested();
38
- }
39
-
40
- const getResults = () => {
41
- let result: React.ReactElement[] = [];
42
- searchResults.forEach((p: any) => {
43
- result.push(<Grid size={{ xs: 12, md: 4 }}>
44
- <a href="about:blank" onClick={(e) => { e.preventDefault(); props.onStockSelect(p.large) }}>
45
- <img
46
- src={p.thumbnail}
47
- alt="stock"
48
- style={{
49
- width: '100%',
50
- height: 'auto',
51
- maxWidth: '100%',
52
- display: 'block'
53
- }}
54
- />
55
- </a>
56
- <div>
57
- <i style={{ fontSize: 12 }}>
58
- <a href={p.url} target="_blank" rel="noreferrer noopener">{Locale.label("stockPhotos.photoBy")}</a> <a href={p.photographerUrl} target="_blank" rel="noreferrer noopener">{p.photographer}</a></i>
59
- </div>
60
- </Grid>);
61
- })
62
- return result;
63
- }
64
-
65
- const getSuggested = () => {
66
- let result: React.ReactElement[] = [];
67
- images.forEach((img: any) => {
68
- result.push(<Grid size={{ xs: 12, md: 4 }}>
69
- <a href="about:blank" onClick={(e) => { e.preventDefault(); props.onSelect(contentRoot + "/" + img) }}>
70
- <img
71
- src={contentRoot + "/" + img}
72
- alt="stock"
73
- style={{
74
- width: '100%',
75
- height: 'auto',
76
- maxWidth: '100%',
77
- display: 'block'
78
- }}
79
- />
80
- </a>
81
- </Grid>);
82
- })
83
- return result;
84
- }
85
-
86
- return (<>
87
- <TextField fullWidth name="personAddText" label="Search Term" value={searchText} onChange={handleChange}
88
- InputProps={{ endAdornment: <Button variant="contained" id="searchButton" data-testid="search-button" onClick={handleSearch}>Search</Button> }}
89
- />
90
- {searchResults && <div>{Locale.label("stockPhotos.providedBy")} <a href="https://pexels.com">Pexels</a>.</div>}
91
- <Grid container spacing={3} alignItems="center">
92
- {getImages()}
93
- </Grid>
94
-
95
- </>);
96
- };
@@ -1,2 +0,0 @@
1
- export * from "./GalleryModal";
2
- export * from "./StockPhotos";
@@ -1,11 +0,0 @@
1
- import React from "react";
2
-
3
- interface Props {
4
- children: React.ReactNode
5
- }
6
-
7
- export const Banner = (props:Props) => {
8
- return (<div id="banner">
9
- {props.children}
10
- </div>);
11
- }
@@ -1,101 +0,0 @@
1
- 'use client'
2
- import React, { useState, useRef } from "react";
3
- import { Box, Button, Icon, Paper, Popper, Grow, ClickAwayListener } from "@mui/material";
4
- import { NavItem } from "../wrapper/NavItem";
5
-
6
- interface Props {
7
- label: string;
8
- menuItems: { url: string; icon: string; label: string }[];
9
- onNavigate: (url: string) => void;
10
- }
11
-
12
- export const PrimaryMenu = (props: Props) => {
13
- const [open, setOpen] = useState(false);
14
- const anchorRef = useRef<HTMLButtonElement>(null);
15
-
16
- const handleToggle = () => {
17
- setOpen((prevOpen) => !prevOpen);
18
- };
19
-
20
- const handleClose = (event: Event | React.SyntheticEvent) => {
21
- if (
22
- anchorRef.current &&
23
- anchorRef.current.contains(event.target as HTMLElement)
24
- ) {
25
- return;
26
- }
27
- setOpen(false);
28
- };
29
-
30
- const handleItemClick = (url: string) => {
31
- setOpen(false);
32
- props.onNavigate(url);
33
- };
34
-
35
- const getNavItems = () => {
36
- let result: React.ReactElement[] = [];
37
- props.menuItems.forEach((item) => {
38
- result.push(
39
- <NavItem
40
- url={item.url}
41
- label={item.label.toUpperCase()}
42
- icon={item.icon}
43
- key={item.url}
44
- onNavigate={handleItemClick}
45
- />
46
- );
47
- });
48
- return result;
49
- };
50
-
51
- return (
52
- <>
53
- <Button
54
- ref={anchorRef}
55
- onClick={handleToggle}
56
- color="inherit"
57
- endIcon={<Icon>expand_more</Icon>}
58
- id="primaryNavButton"
59
- >
60
- <img src="/images/logo-icon.png" alt="Primary Menu" />
61
- <h2>{props.label}</h2>
62
- </Button>
63
-
64
- <Popper
65
- open={open}
66
- anchorEl={anchorRef.current}
67
- role={undefined}
68
- placement="bottom-end"
69
- transition
70
- disablePortal={true}
71
- style={{ zIndex: 1300, color: "#FFF" }}
72
- >
73
- {({ TransitionProps }) => (
74
- <Grow {...TransitionProps}>
75
- <Paper
76
- elevation={3}
77
- sx={{
78
- backgroundColor: "var(--c1)",
79
- color: "#FFF",
80
- minWidth: 300,
81
- paddingY: "10px",
82
- filter: "drop-shadow(0px 2px 8px rgba(0,0,0,0.32))",
83
- }}
84
- >
85
- <ClickAwayListener onClickAway={handleClose}>
86
- <Box
87
- sx={{
88
- "& a": { color: "#FFF", textDecoration: "none" },
89
- "& a:hover": { color: "#DDD" },
90
- }}
91
- >
92
- {getNavItems()}
93
- </Box>
94
- </ClickAwayListener>
95
- </Paper>
96
- </Grow>
97
- )}
98
- </Popper>
99
- </>
100
- );
101
- };
@@ -1,23 +0,0 @@
1
- import { Chip } from "@mui/material";
2
-
3
- interface Props {
4
- label: string,
5
- menuItems: { url: string, label: string }[]
6
- onNavigate: (url: string) => void;
7
- }
8
-
9
- export const SecondaryMenu= (props:Props) => {
10
-
11
- const getItems = () => {
12
- const result:any[] = [];
13
- props.menuItems.forEach((item, index) => {
14
- if (item.label === props.label) result.push(<Chip key={item.url} onClick={() => props.onNavigate(item.url)} label={item.label} component="a" variant="filled" clickable style={{backgroundColor:"var(--c1d2)", color:"#FFF", fontSize:16}} />);
15
- else result.push(<a key={item.url} href="about:blank" onClick={(e) => { e.preventDefault(); props.onNavigate(item.url) }} style={{ color:"#FFF", textDecoration: "none", marginLeft:10, marginRight:10}}>{item.label}</a>);
16
- });
17
- return result;
18
- }
19
-
20
- return (<div id="secondaryMenu">
21
- {getItems()}
22
- </div>)
23
- }
@@ -1,40 +0,0 @@
1
- import React from "react";
2
- import { Box, Button, Icon, Menu } from "@mui/material";
3
- import { NavItem } from "../wrapper/NavItem";
4
-
5
- interface Props {
6
- label: string,
7
- menuItems: { url: string, label: string }[]
8
- onNavigate: (url: string) => void;
9
- }
10
-
11
- export const SecondaryMenuAlt = (props:Props) => {
12
- const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
13
- const handleClose = () => { setAnchorEl(null); };
14
- const open = Boolean(anchorEl);
15
-
16
- const handleClick = (e: React.MouseEvent<HTMLElement>) => {
17
- e.preventDefault();
18
- setAnchorEl(e.currentTarget);
19
- };
20
-
21
- const getItems = () => {
22
- const result:any[] = [];
23
- props.menuItems.forEach(item => {
24
- result.push(<NavItem url={item.url} label={item.label} key={item.url} onNavigate={props.onNavigate} icon="people" />);
25
- });
26
- return result;
27
- }
28
-
29
- return (
30
- <div id="secondaryMenuAlt">
31
- <Button onClick={handleClick} color="inherit" aria-controls={open ? "account-menu" : undefined} aria-haspopup="true" aria-expanded={open ? "true" : undefined} endIcon={<Icon>expand_more</Icon>} id="secondaryMenuButton">
32
- <h3 style={{lineHeight:1}}>{props.label}</h3>
33
- </Button>
34
- <Menu anchorEl={anchorEl} id="account-menu" open={anchorEl!==null} onClose={handleClose} transformOrigin={{ horizontal: "right", vertical: "top" }} anchorOrigin={{ horizontal: "right", vertical: "bottom" }} sx={{ "& .MuiBox-root": { borderBottom: 0 } }}>
35
- <Box sx={{ borderBottom: 1, borderColor: "divider" }}>
36
- {getItems()}
37
- </Box>
38
- </Menu>
39
- </div>)
40
- }
@@ -1,205 +0,0 @@
1
- import React from "react";
2
- import { AppBar, Link, Toolbar } from "@mui/material";
3
- import { styled } from "@mui/material/styles";
4
- import { UserMenu } from "../wrapper/UserMenu";
5
- import { PersonHelper } from "../../helpers/PersonHelper";
6
- import { PrimaryMenu } from "./PrimaryMenu";
7
- import { SecondaryMenu } from "./SecondaryMenu";
8
- import { SecondaryMenuAlt } from "./SecondaryMenuAlt";
9
- import { SupportDrawer } from "./SupportDrawer";
10
- import { UserContextInterface } from "@churchapps/helpers";
11
- import { NotificationService } from "../../helpers/NotificationService";
12
-
13
- type Props = {
14
- primaryMenuLabel: string;
15
- primaryMenuItems:{ url: string, icon:string, label: string }[];
16
- secondaryMenuLabel: string;
17
- secondaryMenuItems:{ url: string, label: string }[];
18
- context: UserContextInterface;
19
- appName: string;
20
- onNavigate: (url: string) => void;
21
- }
22
-
23
- export const SiteHeader = React.memo((props:Props) => {
24
- // Initialize NotificationService without subscribing to count changes to prevent re-renders
25
- React.useEffect(() => {
26
- const initializeNotifications = async () => {
27
- if (props.context?.person?.id && props.context?.userChurch?.church?.id) {
28
- const service = NotificationService.getInstance();
29
- await service.initialize(props.context);
30
- }
31
- };
32
-
33
- initializeNotifications();
34
- }, [props.context?.person?.id, props.context?.userChurch?.church?.id]);
35
-
36
- const refresh = React.useCallback(async () => {
37
- // Direct access to NotificationService for refresh functionality
38
- await NotificationService.getInstance().refresh();
39
- }, []);
40
-
41
- // Memoize userName to prevent recreation
42
- const userName = React.useMemo(() => {
43
- if (props.context?.user) {
44
- return `${props.context.user.firstName} ${props.context.user.lastName}`;
45
- }
46
- return '';
47
- }, [props.context?.user?.firstName, props.context?.user?.lastName]);
48
-
49
- // Memoize profilePicture URL
50
- const profilePicture = React.useMemo(() => {
51
- return PersonHelper.getPhotoUrl(props.context?.person);
52
- }, [props.context?.person]);
53
-
54
- // Create a stable context object to prevent UserMenu recreation
55
- const stableContext = React.useMemo(() => {
56
-
57
- if (!props.context) return undefined;
58
-
59
- return {
60
- user: props.context.user,
61
- person: props.context.person,
62
- userChurch: props.context.userChurch,
63
- userChurches: props.context.userChurches,
64
- setUser: props.context.setUser,
65
- setPerson: props.context.setPerson,
66
- setUserChurch: props.context.setUserChurch,
67
- setUserChurches: props.context.setUserChurches
68
- };
69
- }, [
70
- props.context?.user?.id,
71
- props.context?.user?.firstName,
72
- props.context?.user?.lastName,
73
- props.context?.person?.id,
74
- props.context?.userChurch?.church?.id,
75
- props.context?.userChurches,
76
- props.context?.setUser,
77
- props.context?.setPerson,
78
- props.context?.setUserChurch,
79
- props.context?.setUserChurches
80
- ]);
81
-
82
- const CustomAppBar = styled(AppBar)(
83
- ({ theme }) => ({
84
- zIndex: theme.zIndex.drawer + 1,
85
- backgroundColor: "var(--c1, #1565C0)",
86
- transition: theme.transitions.create(["width", "margin"], {
87
- easing: theme.transitions.easing.sharp,
88
- duration: theme.transitions.duration.leavingScreen
89
- }),
90
- "& .MuiIcon-root": { color: "#FFFFFF" }
91
- })
92
- );
93
-
94
- const getRelatedArticles = () => {
95
- let result: any [] = [];
96
- if (props.appName === "B1Admin") {
97
- if (props.primaryMenuLabel === "People") {
98
- if (props.secondaryMenuLabel === "People") result = ["b1admin/adding-people", "b1admin/advanced-search", "b1admin/assigning-roles"];
99
- else if (props.secondaryMenuLabel === "Groups") result = ["b1admin/group-roster", "b1admin/groups", "b1admin/group-calendar"];
100
- else if (props.secondaryMenuLabel === "Attendance") result = ["b1admin/attendance", "b1admin/checkin"];
101
- }
102
- else if (props.primaryMenuLabel === "Donations") {
103
- if (props.secondaryMenuLabel === "Summary") result = ["b1admin/donation-report"];
104
- else if (props.secondaryMenuLabel === "Batches" || props.secondaryMenuLabel === "Funds") result = ["b1admin/giving", "b1admin/manual-input"];
105
- }
106
- else if (props.primaryMenuLabel === "Serving") {
107
- if (props.secondaryMenuLabel === "Plans") result = ["b1admin/plans"];
108
- else if (props.secondaryMenuLabel === "Tasks") result = ["b1admin/tasks", "b1admin/automations"];
109
- }
110
- else if (props.primaryMenuLabel === "Settings") {
111
- if (props.secondaryMenuLabel === "Settings") result = ["b1admin/assigning-roles", "b1admin/exporting-data", "b1admin/import-csv", "b1admin/import-from-breeze"];
112
- else if (props.secondaryMenuLabel === "Forms") result = ["b1admin/forms"];
113
- }
114
- } else if (props.appName === "B1") {
115
- if (props.primaryMenuLabel === "Mobile App") result = ["b1/admin/portal", "b1/mobile/setup"];
116
- else if (props.primaryMenuLabel === "Website") result = ["b1/admin/portal", "b1/admin/website-elements", "b1/admin/website-setup"];
117
- else if (props.primaryMenuLabel === "Sermons") result = ["b1/admin/sermons", "b1/admin/stream-setup"];
118
- else if (props.primaryMenuLabel === "Calendars") result = ["b1/portal/calendars"];
119
- }
120
- return result;
121
- }
122
-
123
- /*<Typography variant="h6" noWrap>{UserHelper.currentUserChurch?.church?.name || ""}</Typography>*/
124
- return (<>
125
- <div id="site-header" style={{
126
- '--c1': '#1565C0',
127
- '--c1d1': '#1358AD',
128
- '--c1d2': '#114A99',
129
- '--c1l2': '#568BDA',
130
- backgroundColor:"var(--c1)",
131
- color: "#FFF"
132
- } as React.CSSProperties}>
133
- <CustomAppBar id="site-app-bar" position="absolute">
134
- <Toolbar id="site-toolbar" sx={{ pr: "24px", backgroundColor: "var(--c1)", minHeight: "64px !important" }}>
135
- <PrimaryMenu label={props.primaryMenuLabel} menuItems={props.primaryMenuItems} onNavigate={props.onNavigate} />
136
- <SecondaryMenu label={props.secondaryMenuLabel} menuItems={props.secondaryMenuItems} onNavigate={props.onNavigate} />
137
- <div id="secondary-menu-container" style={{ flex: 1 }}>
138
- <SecondaryMenuAlt label={props.secondaryMenuLabel} menuItems={props.secondaryMenuItems} onNavigate={props.onNavigate} />
139
- </div>
140
- {props.context?.user?.id && (
141
- <UserMenu
142
- key="user-menu-stable"
143
- profilePicture={profilePicture}
144
- userName={userName}
145
- context={stableContext}
146
- appName={props.appName}
147
- loadCounts={refresh}
148
- notificationCounts={{notificationCount: 0, pmCount: 0}}
149
- onNavigate={props.onNavigate}
150
- />
151
- )}
152
- {!props.context?.user?.id && <Link id="login-link" href="/login" color="inherit" style={{ textDecoration: "none" }}>Login</Link>}
153
- <SupportDrawer appName={props.appName} relatedArticles={getRelatedArticles()} />
154
- </Toolbar>
155
- </CustomAppBar>
156
- <div id="app-bar-spacer" style={{ height: '64px' }}></div>
157
- </div>
158
- </>
159
- );
160
- }, (prevProps, nextProps) => {
161
- // Custom comparison to prevent unnecessary re-renders
162
-
163
- // Check if essential props have changed
164
- if (prevProps.primaryMenuLabel !== nextProps.primaryMenuLabel ||
165
- prevProps.secondaryMenuLabel !== nextProps.secondaryMenuLabel ||
166
- prevProps.appName !== nextProps.appName) {
167
- return false;
168
- }
169
-
170
- // Check if menu items arrays have changed (shallow comparison)
171
- if (prevProps.primaryMenuItems?.length !== nextProps.primaryMenuItems?.length ||
172
- prevProps.secondaryMenuItems?.length !== nextProps.secondaryMenuItems?.length) {
173
- return false;
174
- }
175
-
176
- // Check if user context has actually changed (deep comparison of essential parts)
177
- const prevUser = prevProps.context?.user;
178
- const nextUser = nextProps.context?.user;
179
-
180
- if (prevUser?.id !== nextUser?.id ||
181
- prevUser?.firstName !== nextUser?.firstName ||
182
- prevUser?.lastName !== nextUser?.lastName) {
183
- return false;
184
- }
185
-
186
- // Check if person context has changed
187
- if (prevProps.context?.person?.id !== nextProps.context?.person?.id) {
188
- return false;
189
- }
190
-
191
- // Check if church context has changed
192
- if (prevProps.context?.userChurch?.church?.id !== nextProps.context?.userChurch?.church?.id) {
193
- return false;
194
- }
195
-
196
- // Check if onNavigate function reference has changed
197
- if (prevProps.onNavigate !== nextProps.onNavigate) {
198
- return false;
199
- }
200
-
201
- // All essential props are the same, skip re-render
202
- return true;
203
- });
204
-
205
- SiteHeader.displayName = 'SiteHeader';