@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,112 +0,0 @@
1
- 'use client';
2
- import React, { useEffect, useState } from "react";
3
- import { Avatar, Box, Button, Drawer, Icon, IconButton, List, ListItem, ListItemButton, ListItemIcon, ListItemText, Typography } from "@mui/material";
4
- import { ApiHelper, UserHelper } from "../../helpers";
5
-
6
- type Props = {
7
- appName: string;
8
- relatedArticles?: string[];
9
- };
10
-
11
- export const SupportDrawer = (props: Props) => {
12
- const [open, setOpen] = useState<boolean>(false);
13
- const [supportContact, setSupportContact] = useState<string>(null);
14
- const [churchLogo, setChurchLogo] = useState<string>(null);
15
- const supportHref = "https://support.churchapps.org/";
16
-
17
- let currentAppName = "";
18
- currentAppName = props.appName.toLowerCase();
19
- if (currentAppName==="b1admin") currentAppName="b1Admin"
20
-
21
- const validateEmail = (email: string) => {
22
- return email.match(
23
- /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
24
- );
25
- };
26
-
27
- const handleChurchSupportClick = () => {
28
- if (validateEmail(supportContact)) {
29
- window.location.href = `mailto:${supportContact}`;
30
- } else {
31
- window.open(`http://${supportContact}`, "_blank").focus();
32
- }
33
- };
34
-
35
- const loadData = () => {
36
- if (UserHelper?.currentUserChurch?.church?.id) {
37
- ApiHelper.get("/settings/public/" + UserHelper.currentUserChurch.church.id, "MembershipApi").then((data: any) => {
38
- const contactRes = data?.supportContact;
39
- if (contactRes && contactRes !== "") setSupportContact(contactRes);
40
-
41
- const logoRes = data?.favicon_16x16;
42
- if (logoRes && logoRes !== "") setChurchLogo(logoRes);
43
- });
44
- }
45
- };
46
-
47
- useEffect(loadData, []);
48
-
49
- return (
50
- <>
51
- <IconButton sx={{ color: "white !important", borderRadius: 2 }} onClick={() => setOpen(true)} aria-label="Open support help" data-testid="support-help-button">
52
- <Icon sx={{ fontSize: "26px !important" }}>help</Icon>
53
- </IconButton>
54
- <Drawer open={open} onClose={() => setOpen(false)} anchor="right" PaperProps={{ sx: { width: { xs: "80%", sm: "40%", md: "30%", lg: "20%" } } }}>
55
- <Box
56
- sx={{ paddingTop: 10, display: "flex", flexDirection: "column", justifyContent: "space-between", minHeight: "98vh" }}>
57
- <Box>
58
- {supportContact ? (
59
- <Box sx={{ display: "flex", justifyContent: "end", alignItems: "center" }}>
60
- <Box sx={{ cursor: "pointer", display: "flex", justifyContent: "center", alignItems: "center", marginRight: 2, borderRadius: 2, padding: "7px", backgroundColor: "#e9e9e9" }} component="div" onClick={() => handleChurchSupportClick()}>
61
- <Avatar variant="rounded" src={churchLogo ? churchLogo : null} sx={{ marginRight: 1, bgcolor: "#568bda", width: 25, height: 25 }}>
62
- <Icon fontSize="small">church</Icon>
63
- </Avatar>
64
- <Typography sx={{ color: "#568bda", fontSize: "13px" }}>Support</Typography>
65
- </Box>
66
- </Box>
67
- ) : null}
68
- <List>
69
- <ListItem disablePadding>
70
- <ListItemButton href={supportHref + currentAppName} target="_blank" rel="noopener noreferrer">
71
- <ListItemIcon sx={{ minWidth: "38px" }}><Icon sx={{ color: "#568bda" }}>article</Icon></ListItemIcon>
72
- <ListItemText sx={{ color: "#568bda" }}>View Documentation</ListItemText>
73
- </ListItemButton>
74
- </ListItem>
75
- </List>
76
- {props?.relatedArticles?.length > 0 ? (
77
- <Box sx={{ marginTop: 2 }}>
78
- <Typography sx={{ color: "#568bda", textDecoration: "underline", textUnderlineOffset: 10, textDecorationThickness: 2, marginLeft: 2 }}>Features Tour</Typography>
79
- <List sx={{ marginTop: 1.5 }}>
80
- {props.relatedArticles.map((a) => {
81
- const parts = a.split("/");
82
- const lastPart = parts[parts.length - 1];
83
- const result = lastPart.replace("-", " ");
84
- return (
85
- <ListItem disablePadding>
86
- <ListItemButton href={supportHref + a} target="_blank" rel="noopener noreferrer">
87
- <ListItemIcon sx={{ minWidth: "35px" }}><Icon sx={{ color: "#568bda" }}>play_circle</Icon></ListItemIcon>
88
- <ListItemText><Typography sx={{ color: "#568bda", textTransform: "capitalize" }} fontSize="15px">{result}</Typography></ListItemText>
89
- </ListItemButton>
90
- </ListItem>
91
- );
92
- })}
93
- </List>
94
- </Box>
95
- ) : null}
96
- </Box>
97
- <Box sx={{ marginRight: 4, marginLeft: 4, marginTop: "auto" }}>
98
- <hr style={{ color: "#568bda", borderColor: "#568bda", marginLeft: -13, marginRight: -13 }} />
99
- <Box sx={{ display: "flex", alignItems: "center", justifyContent: "center", color: "#568bda", marginBottom: 1.5, marginTop: 1.5 }}>
100
- <Avatar src="https://avatars.githubusercontent.com/u/74469593?s=200&v=4" variant="rounded" sx={{ width: 25, height: 25, marginRight: 1 }}>C</Avatar>
101
- <Typography>ChurchApps</Typography>
102
- </Box>
103
- <Box sx={{ display: "flex", justifyContent: "space-between", alignItems: "center", gap: 4, marginBottom: 1 }}>
104
- <Button variant="contained" size="small" startIcon={<Icon fontSize="small">mail</Icon>} fullWidth sx={{ backgroundColor: "#568bda" }} href="mailto:support@churchapps.org">Support</Button>
105
- <Button variant="contained" size="small" startIcon={<Icon fontSize="small">forum</Icon>} fullWidth sx={{ backgroundColor: "#568bda" }} href="https://github.com/orgs/ChurchApps/discussions" target="_blank" rel="noopener noreferrer">Forum</Button>
106
- </Box>
107
- </Box>
108
- </Box>
109
- </Drawer>
110
- </>
111
- );
112
- };
@@ -1,2 +0,0 @@
1
- export { Banner } from "./Banner";
2
- export { SiteHeader } from "./SiteHeader";
@@ -1,20 +0,0 @@
1
- export { ErrorMessages } from "./ErrorMessages";
2
- export { ExportLink } from "./ExportLink";
3
- export { DisplayBox } from "./DisplayBox";
4
- export { FloatingSupport } from "./FloatingSupport";
5
- export { FormSubmissionEdit } from "./FormSubmissionEdit";
6
- export { HelpIcon } from "./HelpIcon";
7
- export { ImageEditor } from "./ImageEditor";
8
- export { InputBox } from "./InputBox";
9
- export { Loading } from "./Loading";
10
- export { Notes } from "./notes/Notes";
11
- export { PageHeader } from "./PageHeader";
12
- export { PersonAvatar } from "./PersonAvatar";
13
- export { QuestionEdit } from "./QuestionEdit";
14
- export { SmallButton } from "./SmallButton";
15
- export { SupportModal } from "./SupportModal";
16
- export { TabPanel } from "./TabPanel";
17
- export * from "./wrapper";
18
- export * from "./notes";
19
- export * from "./header";
20
- export * from "./gallery";
@@ -1,185 +0,0 @@
1
- "use client";
2
-
3
- import React, { useState, useEffect } from "react";
4
- import { ApiHelper, Locale, PersonHelper } from "../../helpers";
5
- import { MessageInterface, UserContextInterface, UserHelper } from "@churchapps/helpers";
6
- import {
7
- Box,
8
- Stack,
9
- TextField,
10
- IconButton,
11
- Paper,
12
- CircularProgress,
13
- Avatar,
14
- Icon
15
- } from "@mui/material";
16
- import { ErrorMessages } from "../ErrorMessages";
17
-
18
- type Props = {
19
- messageId?: string;
20
- onUpdate: () => void;
21
- createConversation: () => Promise<string>;
22
- conversationId?: string;
23
- context: UserContextInterface;
24
- onCancel?: () => void;
25
- };
26
-
27
- export function AddNote({ context, onCancel, ...props }: Props) {
28
- const [message, setMessage] = useState<MessageInterface>();
29
- const [errors, setErrors] = useState<string[]>([]);
30
- const [isSubmitting, setIsSubmitting] = useState(false);
31
- const headerText = props.messageId ? "Edit note" : "Add a note";
32
- const churchId = UserHelper.currentUserChurch?.church?.id || "";
33
-
34
- useEffect(() => {
35
- if (props.messageId) {
36
- ApiHelper.get(`/messages/${churchId}/${props.messageId}`, "MessagingApi")
37
- .then((n: any) => setMessage(n));
38
- } else {
39
- setMessage({ conversationId: props.conversationId, content: "" });
40
- }
41
- return () => setMessage(null);
42
- }, [props.messageId, props.conversationId]);
43
-
44
- const handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>) => {
45
- setErrors([]);
46
- setMessage({ ...message, content: e.target.value });
47
- };
48
-
49
- const validate = () => {
50
- const result = [];
51
- if (!message?.content?.trim()) result.push(Locale.label("notes.validate.content", "Please enter a message"));
52
- setErrors(result);
53
- return result.length === 0;
54
- };
55
-
56
- async function handleSave() {
57
- if (!validate()) return;
58
-
59
- setIsSubmitting(true);
60
- let cId = props.conversationId;
61
- if (!cId) cId = await props.createConversation();
62
-
63
- const m = { ...message, conversationId: cId };
64
- ApiHelper.post("/messages", [m], "MessagingApi")
65
- .then(() => {
66
- props.onUpdate();
67
- setMessage({ ...message, content: "" });
68
- })
69
- .catch((error: any) => {
70
- console.error("Error saving message:", error);
71
- if (error?.message === "Forbidden") {
72
- setErrors(["You can't edit the message sent by others."]);
73
- } else {
74
- setErrors([error?.message || "Failed to save message. Please try again."]);
75
- }
76
- })
77
- .finally(() => setIsSubmitting(false));
78
- }
79
-
80
- async function deleteNote() {
81
- if (!props.messageId) return;
82
- await ApiHelper.delete(`/messages/${churchId}/${props.messageId}`, "MessagingApi");
83
- props.onUpdate();
84
- }
85
-
86
- const deleteFunction = props.messageId ? deleteNote : null;
87
- const image = PersonHelper.getPhotoUrl(context?.person);
88
-
89
- return (
90
- <Box sx={{ width: '100%' }}>
91
- <ErrorMessages errors={errors} />
92
-
93
- <Paper variant="outlined" sx={{ p: 2, bgcolor: 'grey.50', borderColor: 'grey.300' }}>
94
- <Stack direction="row" spacing={2} alignItems="flex-start">
95
- <Avatar
96
- src={image}
97
- alt={context?.person?.name?.display}
98
- sx={{ width: 48, height: 48 }}
99
- />
100
-
101
- <Box sx={{ flex: 1 }}>
102
- <TextField
103
- fullWidth
104
- multiline
105
- rows={2}
106
- name="noteText"
107
- aria-label={headerText}
108
- placeholder={props.messageId ? "Edit your message..." : "Type a message..."}
109
- variant="standard"
110
- value={message?.content || ''}
111
- onChange={handleChange}
112
- disabled={isSubmitting}
113
- InputProps={{
114
- disableUnderline: true,
115
- sx: {
116
- fontSize: '1rem',
117
- '& textarea': { resize: 'vertical', minHeight: '40px' }
118
- }
119
- }}
120
- sx={{
121
- bgcolor: 'white',
122
- borderRadius: 1,
123
- p: 1,
124
- border: '1px solid',
125
- borderColor: 'grey.300',
126
- '&:hover': { borderColor: 'grey.400' },
127
- '&.Mui-focused': { borderColor: 'primary.main' }
128
- }}
129
- />
130
-
131
- {/* Buttons: Cancel (left), Delete + Send (right) */}
132
- <Box sx={{ display: 'flex', alignItems: 'center', mt: 1 }}>
133
- {/* Cancel Button */}
134
- {props.messageId && (
135
- <IconButton
136
- size="small"
137
- onClick={() => {
138
- setMessage({ ...message, content: "" });
139
- onCancel?.();
140
- }}
141
- disabled={isSubmitting}
142
- sx={{ color: 'grey.700' }}
143
- >
144
- <Icon fontSize="small">cancel</Icon>
145
- </IconButton>
146
- )}
147
-
148
- {/* Spacer */}
149
- <Box sx={{ flex: 1 }} />
150
-
151
- {/* Right buttons: Delete + Send */}
152
- <Box sx={{ display: 'flex', gap: 0.5 }}>
153
- {deleteFunction && (
154
- <IconButton
155
- size="small"
156
- onClick={deleteFunction}
157
- disabled={isSubmitting}
158
- sx={{ color: 'error.main' }}
159
- >
160
- <Icon fontSize="small">delete</Icon>
161
- </IconButton>
162
- )}
163
-
164
- <IconButton
165
- size="small"
166
- color="primary"
167
- onClick={handleSave}
168
- disabled={isSubmitting || !message?.content?.trim()}
169
- sx={{
170
- bgcolor: 'primary.main',
171
- color: 'white',
172
- '&:hover': { bgcolor: 'primary.dark' },
173
- '&:disabled': { bgcolor: 'action.disabledBackground', color: 'action.disabled' }
174
- }}
175
- >
176
- {isSubmitting ? <CircularProgress size={18} color="inherit" /> : <Icon fontSize="small">send</Icon>}
177
- </IconButton>
178
- </Box>
179
- </Box>
180
- </Box>
181
- </Stack>
182
- </Paper>
183
- </Box>
184
- );
185
- }
@@ -1,84 +0,0 @@
1
- 'use client';
2
-
3
- import { Icon, IconButton, Stack, Box, Typography } from "@mui/material";
4
- import React, { useState, useEffect } from "react";
5
- import { DateHelper } from "../../helpers"
6
- import { MessageInterface, UserContextInterface } from "@churchapps/helpers"
7
- import { PersonAvatar } from "../PersonAvatar"
8
-
9
- interface Props {
10
- message: MessageInterface;
11
- showEditNote: (noteId?: string) => void;
12
- context: UserContextInterface;
13
- isEditing?: boolean;
14
- hideEdit?: boolean;
15
- }
16
-
17
- export const Note: React.FC<Props> = (props) => {
18
- const [message, setMessage] = useState<MessageInterface>(null);
19
-
20
- useEffect(() => setMessage(props.message), [props.message]);
21
-
22
- if (message === null) return null;
23
- const datePosted = new Date(message.timeUpdated || message.timeSent);
24
- const displayDuration = DateHelper.getDisplayDuration(datePosted);
25
-
26
- const isEdited = message.timeUpdated && message.timeUpdated !== message.timeSent;
27
- const contents = message.content?.split("\n");
28
-
29
- return (
30
- <Box
31
- sx={{
32
- display: 'flex',
33
- gap: 2,
34
- mb: 2,
35
- p: 1,
36
- borderRadius: 1,
37
- bgcolor: props.isEditing ? 'rgba(255, 243, 224, 0.5)' : 'transparent',
38
- border: props.isEditing ? '1px solid #FFA726' : 'none',
39
- transition: 'background-color 0.3s, border 0.3s',
40
- '&:hover': { bgcolor: 'action.hover' }
41
- }}
42
- >
43
- <PersonAvatar person={message.person} size="small" />
44
- <Box sx={{ flex: 1 }}>
45
- <Stack direction="row" justifyContent="space-between" alignItems="flex-start">
46
- <Box sx={{ flex: 1 }}>
47
- <Stack direction="row" alignItems="center" spacing={1} sx={{ mb: 0.5 }}>
48
- <Typography variant="subtitle2" fontWeight="bold">
49
- {message.person?.name?.display}
50
- </Typography>
51
- <Typography variant="caption" color="text.secondary">
52
- {displayDuration}
53
- </Typography>
54
- {isEdited && (
55
- <Typography variant="caption" color="text.secondary">
56
- (edited)
57
- </Typography>
58
- )}
59
- </Stack>
60
- <Box>
61
- {contents.map((c, i) => c ? (
62
- <Typography key={`content-${i}-${c.substring(0, 20)}`} variant="body2" sx={{ mb: 0.5 }}>
63
- {c}
64
- </Typography>
65
- ) : (
66
- <Box key={`empty-${i}`} sx={{ height: '1em' }} />
67
- ))}
68
- </Box>
69
- </Box>
70
- {(message?.id && message.personId === props.context?.person.id && !props.hideEdit) && (
71
- <IconButton
72
- size="small"
73
- aria-label="editNote"
74
- onClick={() => props.showEditNote(message.id)}
75
- sx={{ opacity: 0.7, '&:hover': { opacity: 1 } }}
76
- >
77
- <Icon fontSize="small">edit</Icon>
78
- </IconButton>
79
- )}
80
- </Stack>
81
- </Box>
82
- </Box>
83
- );
84
- };
@@ -1,208 +0,0 @@
1
- import React from "react";
2
- import { Note } from "./Note";
3
- import { AddNote } from "./AddNote";
4
- import { DisplayBox, Loading } from "../";
5
- import { ApiHelper, ArrayHelper, Locale } from "../../helpers";
6
- import { MessageInterface, UserContextInterface } from "@churchapps/helpers";
7
-
8
- interface Props {
9
- //showEditNote: (messageId?: string) => void;
10
- conversationId: string;
11
- createConversation?: () => Promise<string>;
12
- noDisplayBox?: boolean;
13
- context: UserContextInterface;
14
- maxHeight?: any;
15
- refreshKey?: number;
16
- }
17
-
18
- export function Notes(props: Props) {
19
-
20
- const [messages, setMessages] = React.useState<MessageInterface[]>(null)
21
- const [editMessageId, setEditMessageId] = React.useState(null)
22
- const [isInitialLoad, setIsInitialLoad] = React.useState(true)
23
- const [previousMessageCount, setPreviousMessageCount] = React.useState(0)
24
-
25
- // Add CSS for custom scrollbar styling
26
- React.useEffect(() => {
27
- const styleId = 'notes-scrollbar-styles';
28
- if (!document.getElementById(styleId)) {
29
- const style = document.createElement('style');
30
- style.id = styleId;
31
- style.textContent = `
32
- .notes-scroll-container {
33
- scrollbar-width: thin;
34
- scrollbar-color: rgba(0, 0, 0, 0.3) rgba(0, 0, 0, 0.1);
35
- }
36
- .notes-scroll-container::-webkit-scrollbar {
37
- width: 12px;
38
- background: transparent;
39
- }
40
- .notes-scroll-container::-webkit-scrollbar-track {
41
- background: rgba(0, 0, 0, 0.1);
42
- border-radius: 6px;
43
- margin: 4px;
44
- }
45
- .notes-scroll-container::-webkit-scrollbar-thumb {
46
- background: rgba(0, 0, 0, 0.3);
47
- border-radius: 6px;
48
- border: 2px solid transparent;
49
- background-clip: content-box;
50
- }
51
- .notes-scroll-container::-webkit-scrollbar-thumb:hover {
52
- background: rgba(0, 0, 0, 0.5);
53
- background-clip: content-box;
54
- }
55
- .notes-scroll-container::-webkit-scrollbar-corner {
56
- background: transparent;
57
- }
58
- `;
59
- document.head.appendChild(style);
60
- }
61
- }, []);
62
-
63
- const loadNotes = async () => {
64
- try {
65
- const messages: MessageInterface[] = (props.conversationId) ? await ApiHelper.get("/messages/conversation/" + props.conversationId, "MessagingApi") : [];
66
- if (messages.length > 0) {
67
- const peopleIds = ArrayHelper.getIds(messages, "personId");
68
- const people = await ApiHelper.get("/people/basic?ids=" + peopleIds.join(","), "MembershipApi");
69
- messages.forEach(n => {
70
- n.person = ArrayHelper.getOne(people, "id", n.personId);
71
- })
72
- }
73
- setMessages(messages);
74
- setEditMessageId(null);
75
-
76
- // Mark as no longer initial load after first load
77
- if (isInitialLoad) {
78
- setIsInitialLoad(false);
79
- }
80
- } catch (error) {
81
- console.error("❌ Failed to load messages for conversation:", props.conversationId, error);
82
- // Don't clear messages on error - keep showing existing messages
83
- // Only set isInitialLoad to false if this was the first load attempt
84
- if (isInitialLoad) {
85
- setIsInitialLoad(false);
86
- }
87
- }
88
- };
89
-
90
- const getNotes = () => {
91
- if (!messages) return <Loading />
92
- if (messages.length === 0) return <></>
93
- else {
94
- let noteArray: React.ReactNode[] = [];
95
- for (let i = 0; i < messages.length; i++) noteArray.push(<Note message={messages[i]} key={messages[i].id} showEditNote={setEditMessageId} context={props.context} />);
96
- return noteArray;
97
- }
98
- }
99
-
100
- const getNotesWrapper = () => {
101
- const notes = getNotes();
102
- if (props.maxHeight) {
103
- return (
104
- <div
105
- id="notesScroll"
106
- style={{
107
- flex: 1,
108
- minHeight: 0,
109
- overflowY: "auto",
110
- overflowX: "hidden",
111
- padding: "8px 12px",
112
- scrollBehavior: "smooth",
113
- height: "100%"
114
- }}
115
- className="notes-scroll-container"
116
- data-testid="message-scroll-area"
117
- >
118
- <div style={{
119
- display: "flex",
120
- flexDirection: "column",
121
- gap: "8px",
122
- minHeight: "min-content"
123
- }}>
124
- {notes}
125
- </div>
126
- </div>
127
- );
128
- }
129
- else return notes;
130
- }
131
-
132
- React.useEffect(() => { loadNotes() }, [props.conversationId, props.refreshKey]); //eslint-disable-line
133
-
134
- // Simply reload notes when refreshKey changes
135
- // This is triggered by the parent component when WebSocket messages arrive
136
-
137
- // Auto-scroll to bottom only when new messages are added (not on initial load)
138
- React.useEffect(() => {
139
- if (props.maxHeight && messages?.length > 0 && !isInitialLoad) {
140
- const currentMessageCount = messages.length;
141
-
142
- // Only auto-scroll if messages were added
143
- if (currentMessageCount > previousMessageCount) {
144
- // Use requestAnimationFrame for smoother scrolling
145
- requestAnimationFrame(() => {
146
- const element = window?.document?.getElementById("notesScroll");
147
- if (element) {
148
- element.scrollTop = element.scrollHeight;
149
- }
150
- });
151
- }
152
-
153
- setPreviousMessageCount(currentMessageCount);
154
- } else if (messages?.length > 0 && isInitialLoad) {
155
- // On initial load, just set the previous count without scrolling
156
- setPreviousMessageCount(messages.length);
157
- }
158
- }, [messages, props.maxHeight, isInitialLoad, previousMessageCount]);
159
-
160
- let result = props.maxHeight ? (
161
- <div style={{
162
- height: "100%",
163
- display: "flex",
164
- flexDirection: "column",
165
- overflow: "hidden",
166
- minHeight: 0
167
- }}>
168
- {/* Messages area - scrollable */}
169
- <div style={{
170
- flex: 1,
171
- minHeight: 0,
172
- display: "flex",
173
- flexDirection: "column",
174
- overflow: "hidden"
175
- }}>
176
- {getNotesWrapper()}
177
- </div>
178
-
179
- {/* Input area - always visible at bottom */}
180
- {messages && (
181
- <div style={{
182
- flexShrink: 0,
183
- borderTop: "1px solid #e0e0e0",
184
- backgroundColor: "#fafafa",
185
- padding: "12px",
186
- minHeight: "auto",
187
- maxHeight: "200px"
188
- }}>
189
- <AddNote
190
- context={props.context}
191
- conversationId={props.conversationId}
192
- onUpdate={loadNotes}
193
- createConversation={props.createConversation}
194
- messageId={editMessageId}
195
- />
196
- </div>
197
- )}
198
- </div>
199
- ) : (
200
- <>
201
- {getNotesWrapper()}
202
- {messages && (<AddNote context={props.context} conversationId={props.conversationId} onUpdate={loadNotes} createConversation={props.createConversation} messageId={editMessageId} />)}
203
- </>
204
- );
205
-
206
- if (props.noDisplayBox) return result;
207
- else return (<DisplayBox id="notesBox" data-testid="notes-box" headerIcon="sticky_note_2" headerText={Locale.label("notes.notes", "Notes")}>{result}</DisplayBox>);
208
- };
@@ -1,3 +0,0 @@
1
- export { Notes } from "./Notes";
2
- export { Note } from "./Note";
3
- export { AddNote } from "./AddNote";
@@ -1,18 +0,0 @@
1
- "use client";
2
-
3
- import React from "react";
4
- import { ApiHelper, UserHelper, CommonEnvironmentHelper, LoginUserChurchInterface } from "@churchapps/helpers";
5
- import { NavItem } from "./NavItem";
6
-
7
- export interface Props { appName: string; currentUserChurch: LoginUserChurchInterface; onNavigate: (url: string) => void; }
8
-
9
- export const AppList: React.FC<Props> = props => {
10
- const jwt = ApiHelper.getConfig("MembershipApi").jwt;
11
- const churchId = UserHelper.currentUserChurch.church.id;
12
- return (
13
- <>
14
- <NavItem url={`${CommonEnvironmentHelper.B1Root.replace("{key}", props.currentUserChurch.church.subDomain)}/login?jwt=${jwt}&churchId=${churchId}`} selected={props.appName === "B1.church"} external={true} label="B1.Church" icon="logout" onNavigate={props.onNavigate} />
15
- <NavItem url={`${CommonEnvironmentHelper.LessonsRoot}/login?jwt=${jwt}&churchId=${churchId}`} selected={props.appName === "Lessons.church"} external={true} label="Lessons.church" icon="logout" onNavigate={props.onNavigate} />
16
- </>
17
- );
18
- }