@churchapps/apphelper 0.4.17 → 0.4.18

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 (137) hide show
  1. package/dist/components/FormCardPayment.js +2 -2
  2. package/dist/components/FormCardPayment.js.map +1 -1
  3. package/dist/components/FormSubmissionEdit.d.ts.map +1 -1
  4. package/dist/components/FormSubmissionEdit.js +4 -5
  5. package/dist/components/FormSubmissionEdit.js.map +1 -1
  6. package/dist/components/PageHeader.d.ts +15 -0
  7. package/dist/components/PageHeader.d.ts.map +1 -0
  8. package/dist/components/PageHeader.js +41 -0
  9. package/dist/components/PageHeader.js.map +1 -0
  10. package/dist/components/PersonAvatar.d.ts +12 -0
  11. package/dist/components/PersonAvatar.d.ts.map +1 -0
  12. package/dist/components/PersonAvatar.js +55 -0
  13. package/dist/components/PersonAvatar.js.map +1 -0
  14. package/dist/components/header/SiteHeader.d.ts +2 -1
  15. package/dist/components/header/SiteHeader.d.ts.map +1 -1
  16. package/dist/components/header/SiteHeader.js +100 -4
  17. package/dist/components/header/SiteHeader.js.map +1 -1
  18. package/dist/components/header/SupportDrawer.js.map +1 -1
  19. package/dist/components/index.d.ts +2 -0
  20. package/dist/components/index.d.ts.map +1 -1
  21. package/dist/components/index.js +2 -0
  22. package/dist/components/index.js.map +1 -1
  23. package/dist/components/notes/AddNote.d.ts.map +1 -1
  24. package/dist/components/notes/AddNote.js +45 -7
  25. package/dist/components/notes/AddNote.js.map +1 -1
  26. package/dist/components/notes/Note.d.ts.map +1 -1
  27. package/dist/components/notes/Note.js +6 -6
  28. package/dist/components/notes/Note.js.map +1 -1
  29. package/dist/components/notes/Notes.d.ts.map +1 -1
  30. package/dist/components/notes/Notes.js +120 -20
  31. package/dist/components/notes/Notes.js.map +1 -1
  32. package/dist/components/wrapper/ChurchList.d.ts.map +1 -1
  33. package/dist/components/wrapper/ChurchList.js +44 -6
  34. package/dist/components/wrapper/ChurchList.js.map +1 -1
  35. package/dist/components/wrapper/NewPrivateMessage.d.ts.map +1 -1
  36. package/dist/components/wrapper/NewPrivateMessage.js +28 -21
  37. package/dist/components/wrapper/NewPrivateMessage.js.map +1 -1
  38. package/dist/components/wrapper/Notifications.d.ts.map +1 -1
  39. package/dist/components/wrapper/Notifications.js +47 -20
  40. package/dist/components/wrapper/Notifications.js.map +1 -1
  41. package/dist/components/wrapper/PrivateMessageDetails.d.ts +1 -0
  42. package/dist/components/wrapper/PrivateMessageDetails.d.ts.map +1 -1
  43. package/dist/components/wrapper/PrivateMessageDetails.js +53 -4
  44. package/dist/components/wrapper/PrivateMessageDetails.js.map +1 -1
  45. package/dist/components/wrapper/PrivateMessages.d.ts.map +1 -1
  46. package/dist/components/wrapper/PrivateMessages.js +360 -41
  47. package/dist/components/wrapper/PrivateMessages.js.map +1 -1
  48. package/dist/components/wrapper/UserMenu.d.ts.map +1 -1
  49. package/dist/components/wrapper/UserMenu.js +163 -26
  50. package/dist/components/wrapper/UserMenu.js.map +1 -1
  51. package/dist/components/wrapper/index.d.ts +2 -1
  52. package/dist/components/wrapper/index.d.ts.map +1 -1
  53. package/dist/components/wrapper/index.js +2 -1
  54. package/dist/components/wrapper/index.js.map +1 -1
  55. package/dist/helpers/ArrayHelper.d.ts.map +1 -1
  56. package/dist/helpers/ArrayHelper.js +0 -1
  57. package/dist/helpers/ArrayHelper.js.map +1 -1
  58. package/dist/helpers/ErrorHelper.js +1 -1
  59. package/dist/helpers/ErrorHelper.js.map +1 -1
  60. package/dist/helpers/EventHelper.d.ts.map +1 -1
  61. package/dist/helpers/EventHelper.js +0 -3
  62. package/dist/helpers/EventHelper.js.map +1 -1
  63. package/dist/helpers/Locale.d.ts +1 -1
  64. package/dist/helpers/Locale.d.ts.map +1 -1
  65. package/dist/helpers/Locale.js +7 -2
  66. package/dist/helpers/Locale.js.map +1 -1
  67. package/dist/helpers/NotificationService.d.ts +56 -0
  68. package/dist/helpers/NotificationService.d.ts.map +1 -0
  69. package/dist/helpers/NotificationService.js +176 -0
  70. package/dist/helpers/NotificationService.js.map +1 -0
  71. package/dist/helpers/SocketHelper.d.ts.map +1 -1
  72. package/dist/helpers/SocketHelper.js +22 -17
  73. package/dist/helpers/SocketHelper.js.map +1 -1
  74. package/dist/helpers/UserHelper.js +2 -2
  75. package/dist/helpers/UserHelper.js.map +1 -1
  76. package/dist/helpers/index.d.ts +2 -0
  77. package/dist/helpers/index.d.ts.map +1 -1
  78. package/dist/helpers/index.js +1 -0
  79. package/dist/helpers/index.js.map +1 -1
  80. package/dist/hooks/index.d.ts +2 -0
  81. package/dist/hooks/index.d.ts.map +1 -1
  82. package/dist/hooks/index.js +1 -0
  83. package/dist/hooks/index.js.map +1 -1
  84. package/dist/hooks/useNotifications.d.ts +30 -0
  85. package/dist/hooks/useNotifications.d.ts.map +1 -0
  86. package/dist/hooks/useNotifications.js +79 -0
  87. package/dist/hooks/useNotifications.js.map +1 -0
  88. package/dist/public/css/styles.css +6 -2
  89. package/package.json +1 -1
  90. package/public/css/styles.css +6 -2
  91. package/src/components/FormCardPayment.tsx +2 -2
  92. package/src/components/FormSubmissionEdit.tsx +5 -6
  93. package/src/components/PageHeader.tsx +107 -0
  94. package/src/components/PersonAvatar.tsx +78 -0
  95. package/src/components/header/SiteHeader.tsx +131 -8
  96. package/src/components/header/SupportDrawer.tsx +1 -1
  97. package/src/components/index.tsx +2 -0
  98. package/src/components/notes/AddNote.tsx +105 -19
  99. package/src/components/notes/Note.tsx +43 -22
  100. package/src/components/notes/Notes.tsx +160 -21
  101. package/src/components/wrapper/ChurchList.tsx +45 -5
  102. package/src/components/wrapper/NewPrivateMessage.tsx +181 -44
  103. package/src/components/wrapper/Notifications.tsx +164 -29
  104. package/src/components/wrapper/PrivateMessageDetails.tsx +100 -13
  105. package/src/components/wrapper/PrivateMessages.tsx +539 -65
  106. package/src/components/wrapper/UserMenu.tsx +217 -34
  107. package/src/components/wrapper/index.tsx +3 -2
  108. package/src/helpers/ArrayHelper.ts +0 -1
  109. package/src/helpers/ErrorHelper.ts +1 -1
  110. package/src/helpers/EventHelper.ts +0 -3
  111. package/src/helpers/Locale.ts +7 -2
  112. package/src/helpers/NotificationService.ts +211 -0
  113. package/src/helpers/SocketHelper.ts +23 -17
  114. package/src/helpers/UserHelper.ts +2 -2
  115. package/src/helpers/index.ts +2 -0
  116. package/src/hooks/index.ts +2 -0
  117. package/src/hooks/useNotifications.ts +94 -0
  118. package/dist/components/wrapper/Drawers.d.ts +0 -5
  119. package/dist/components/wrapper/Drawers.d.ts.map +0 -1
  120. package/dist/components/wrapper/Drawers.js +0 -49
  121. package/dist/components/wrapper/Drawers.js.map +0 -1
  122. package/dist/components/wrapper/SiteWrapper.d.ts +0 -15
  123. package/dist/components/wrapper/SiteWrapper.d.ts.map +0 -1
  124. package/dist/components/wrapper/SiteWrapper.js +0 -60
  125. package/dist/components/wrapper/SiteWrapper.js.map +0 -1
  126. package/dist/components/wrapper/TabPanel.d.ts +0 -9
  127. package/dist/components/wrapper/TabPanel.d.ts.map +0 -1
  128. package/dist/components/wrapper/TabPanel.js +0 -17
  129. package/dist/components/wrapper/TabPanel.js.map +0 -1
  130. package/dist/helpers/ApiHelper.d.ts +0 -18
  131. package/dist/helpers/ApiHelper.d.ts.map +0 -1
  132. package/dist/helpers/ApiHelper.js +0 -119
  133. package/dist/helpers/ApiHelper.js.map +0 -1
  134. package/src/components/wrapper/Drawers.tsx +0 -62
  135. package/src/components/wrapper/SiteWrapper.tsx +0 -110
  136. package/src/components/wrapper/TabPanel.tsx +0 -32
  137. package/src/helpers/ApiHelper.ts +0 -127
@@ -1,8 +1,30 @@
1
1
  "use client";
2
2
 
3
3
  import React, { useState } from "react";
4
- import { ApiHelper } from "../../helpers/ApiHelper";
5
- import { Box, Icon, Stack } from "@mui/material";
4
+ import { ApiHelper } from "@churchapps/helpers";
5
+ import {
6
+ Box,
7
+ Icon,
8
+ Stack,
9
+ Typography,
10
+ Paper,
11
+ List,
12
+ ListItem,
13
+ ListItemAvatar,
14
+ ListItemText,
15
+ Avatar,
16
+ Chip,
17
+ Divider,
18
+ IconButton,
19
+ Skeleton,
20
+ useTheme
21
+ } from "@mui/material";
22
+ import {
23
+ Notifications as NotificationsIcon,
24
+ Task as TaskIcon,
25
+ Assignment as AssignmentIcon,
26
+ OpenInNew as OpenInNewIcon
27
+ } from "@mui/icons-material";
6
28
  import { NotificationInterface, UserContextInterface } from "@churchapps/helpers";
7
29
  import { DateHelper } from "../../helpers";
8
30
  import { Navigate } from "react-router-dom";
@@ -16,10 +38,14 @@ interface Props {
16
38
 
17
39
  export const Notifications: React.FC<Props> = (props) => {
18
40
  const [notifications, setNotifications] = useState<NotificationInterface[]>([]);
41
+ const [isLoading, setIsLoading] = useState(true);
42
+ const theme = useTheme();
19
43
 
20
44
  const loadData = async () => {
45
+ setIsLoading(true);
21
46
  const n: NotificationInterface[] = await ApiHelper.get("/notifications/my", "MessagingApi");
22
47
  setNotifications(n);
48
+ setIsLoading(false);
23
49
  props.onUpdate();
24
50
  }
25
51
 
@@ -47,42 +73,151 @@ export const Notifications: React.FC<Props> = (props) => {
47
73
  props.onNavigate(path);
48
74
  }
49
75
  else {
50
- console.log("REDIRECTING TO", appUrl + path)
51
76
  window.open(appUrl + path, "_blank");
52
77
  }
53
78
  }
54
79
 
55
- const getMainLinks = () => {
56
- let result: React.ReactElement[] = [];
57
- notifications.forEach(notification => {
58
- let datePosted = new Date(notification.timeSent);
59
- const displayDuration = DateHelper.getDisplayDuration(datePosted);
80
+ const getNotificationIcon = (contentType: string) => {
81
+ switch (contentType) {
82
+ case "task": return <TaskIcon />;
83
+ case "assignment": return <AssignmentIcon />;
84
+ default: return <NotificationsIcon />;
85
+ }
86
+ }
60
87
 
61
- result.push(
62
- <div className="note" style={{ cursor: "pointer" }} onClick={(e) => { e.preventDefault(); }} role="button" tabIndex={0} onKeyDown={(e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); handleClick(notification); } }}>
63
- <Box sx={{ width: "100%" }} className="note-contents">
64
- <Stack direction="row" justifyContent="space-between">
65
- <div style={{width:"100%"}} onClick={(e) => { e.preventDefault(); handleClick(notification); }} role="button" tabIndex={0} onKeyDown={(e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); handleClick(notification); } }} aria-label={`View notification: ${notification.message}`}>
66
- <span className="text-grey" style={{float:"right"}}>{displayDuration}</span>
67
- <p>
68
- <Icon>notifications</Icon>
69
- {notification.message}
70
- <span>{notification?.link ? <a href={notification.link} onClick={(e) => { e.stopPropagation() }} target="_blank">View Details</a> : null}</span>
71
- </p>
72
- </div>
73
- </Stack>
74
- </Box>
75
- </div>
88
+ const getNotificationList = () => {
89
+ if (notifications.length === 0) {
90
+ return (
91
+ <Box sx={{ textAlign: 'center', py: 4 }}>
92
+ <NotificationsIcon sx={{ fontSize: 48, color: 'grey.400', mb: 2 }} />
93
+ <Typography variant="h6" color="textSecondary">
94
+ No notifications
95
+ </Typography>
96
+ <Typography variant="body2" color="textSecondary">
97
+ You're all caught up!
98
+ </Typography>
99
+ </Box>
76
100
  );
77
- })
78
- return result;
101
+ }
102
+
103
+ return (
104
+ <List sx={{ width: '100%' }}>
105
+ {notifications.map((notification, index) => {
106
+ let datePosted = new Date(notification.timeSent);
107
+ const displayDuration = DateHelper.getDisplayDuration(datePosted);
108
+ const isUnread = notification.isNew;
109
+
110
+ return (
111
+ <React.Fragment key={notification.id}>
112
+ <ListItem
113
+ component="button"
114
+ onClick={() => handleClick(notification)}
115
+ sx={{
116
+ alignItems: 'flex-start',
117
+ py: 2,
118
+ cursor: 'pointer',
119
+ bgcolor: isUnread ? 'action.hover' : 'transparent',
120
+ '&:hover': {
121
+ bgcolor: 'action.hover'
122
+ },
123
+ borderRadius: 1,
124
+ mb: 0.5
125
+ }}
126
+ >
127
+ <ListItemAvatar>
128
+ <Avatar
129
+ sx={{
130
+ bgcolor: isUnread ? 'primary.main' : 'grey.400',
131
+ width: 48,
132
+ height: 48
133
+ }}
134
+ >
135
+ {getNotificationIcon(notification.contentType)}
136
+ </Avatar>
137
+ </ListItemAvatar>
138
+ <ListItemText
139
+ primary={
140
+ <Stack direction="row" justifyContent="space-between" alignItems="flex-start">
141
+ <Typography
142
+ variant="body1"
143
+ sx={{
144
+ fontWeight: isUnread ? 600 : 400,
145
+ flex: 1,
146
+ pr: 1
147
+ }}
148
+ >
149
+ {notification.message}
150
+ </Typography>
151
+ <Stack direction="row" alignItems="center" spacing={1}>
152
+ {isUnread && (
153
+ <Chip
154
+ size="small"
155
+ label="New"
156
+ color="primary"
157
+ sx={{ height: 20, fontSize: '0.7rem' }}
158
+ />
159
+ )}
160
+ <Typography variant="caption" color="textSecondary">
161
+ {displayDuration}
162
+ </Typography>
163
+ </Stack>
164
+ </Stack>
165
+ }
166
+ secondary={
167
+ notification.link && (
168
+ <Box sx={{ mt: 1 }}>
169
+ <IconButton
170
+ size="small"
171
+ onClick={(e) => {
172
+ e.stopPropagation();
173
+ window.open(notification.link, '_blank');
174
+ }}
175
+ sx={{ p: 0.5 }}
176
+ >
177
+ <OpenInNewIcon fontSize="small" />
178
+ <Typography variant="caption" sx={{ ml: 0.5 }}>
179
+ View Details
180
+ </Typography>
181
+ </IconButton>
182
+ </Box>
183
+ )
184
+ }
185
+ />
186
+ </ListItem>
187
+ {index < notifications.length - 1 && <Divider variant="inset" component="li" />}
188
+ </React.Fragment>
189
+ );
190
+ })}
191
+ </List>
192
+ );
79
193
  }
80
194
 
81
- React.useEffect(() => { console.log("RELOADED NOTIFICATIONS") }, []);
82
195
 
83
196
  return (
84
- <>
85
- {getMainLinks()}
86
- </>
197
+ <Paper elevation={0} sx={{ height: '100%', display: 'flex', flexDirection: 'column' }}>
198
+ <Box sx={{ p: 2, borderBottom: 1, borderColor: 'divider' }}>
199
+ <Typography variant="h6" component="h2">
200
+ Notifications
201
+ </Typography>
202
+ </Box>
203
+
204
+ <Box sx={{ flex: 1, overflow: 'auto' }}>
205
+ {isLoading ? (
206
+ <Box sx={{ p: 2 }}>
207
+ {[...Array(3)].map((_, index) => (
208
+ <Box key={index} sx={{ display: 'flex', alignItems: 'center', mb: 2 }}>
209
+ <Skeleton variant="circular" width={48} height={48} sx={{ mr: 2 }} />
210
+ <Box sx={{ flex: 1 }}>
211
+ <Skeleton variant="text" width="80%" height={24} />
212
+ <Skeleton variant="text" width="40%" height={20} />
213
+ </Box>
214
+ </Box>
215
+ ))}
216
+ </Box>
217
+ ) : (
218
+ getNotificationList()
219
+ )}
220
+ </Box>
221
+ </Paper>
87
222
  );
88
223
  };
@@ -1,27 +1,114 @@
1
1
  "use client";
2
2
 
3
3
  import React from "react";
4
- import { SmallButton } from "../SmallButton";
4
+ import {
5
+ Paper,
6
+ Box,
7
+ Typography,
8
+ Stack,
9
+ IconButton,
10
+ Avatar,
11
+ Divider,
12
+ useTheme
13
+ } from "@mui/material";
14
+ import { ArrowBack as ArrowBackIcon } from "@mui/icons-material";
5
15
  import { PrivateMessageInterface, UserContextInterface } from "@churchapps/helpers";
6
16
  import { Notes } from "../notes/Notes";
7
- import { Locale } from "../../helpers";
17
+ import { ApiHelper, Locale, NotificationService } from "../../helpers";
18
+ import { PersonAvatar } from "../PersonAvatar";
8
19
 
9
20
  interface Props {
10
21
  context: UserContextInterface;
11
22
  privateMessage: PrivateMessageInterface;
12
23
  onBack: () => void
13
24
  refreshKey: number;
25
+ onMessageRead?: () => void;
14
26
  }
15
27
 
16
- export const PrivateMessageDetails: React.FC<Props> = (props) => (
17
- <>
18
- <div style={{ paddingLeft: 10, paddingRight: 10, paddingBottom: 10 }}>
19
- <span style={{ float: "right" }}>
20
- <SmallButton icon="chevron_left" text="Back" onClick={props.onBack} />
21
- </span>
22
- {Locale.label("wrapper.chatWith")} {props.privateMessage.person.name.display}
23
- </div>
24
- <Notes maxHeight={"50vh"} context={props.context} conversationId={props.privateMessage.conversationId} noDisplayBox={true} refreshKey={props.refreshKey} />
25
- </>
26
- );
28
+ export const PrivateMessageDetails: React.FC<Props> = (props) => {
29
+ const theme = useTheme();
30
+
31
+ // Clear notification when conversation is opened
32
+ React.useEffect(() => {
33
+ const clearNotification = async () => {
34
+ if (props.privateMessage.notifyPersonId === props.context.person.id) {
35
+ try {
36
+ console.log("Marking private message as read:", props.privateMessage.id);
37
+
38
+ // Clear the notification by getting the private message details
39
+ await ApiHelper.get(`/privateMessages/${props.privateMessage.id}`, "MessagingApi");
40
+
41
+ // Manually refresh notification counts to ensure immediate UI update
42
+ const notificationService = NotificationService.getInstance();
43
+ await notificationService.refresh();
44
+
45
+ console.log("Private message marked as read and notifications refreshed");
46
+
47
+ // Notify parent component that message was marked as read
48
+ if (props.onMessageRead) {
49
+ props.onMessageRead();
50
+ }
51
+ } catch (error) {
52
+ console.error("Failed to clear notification:", error);
53
+ }
54
+ }
55
+ };
56
+
57
+ clearNotification();
58
+ }, [props.privateMessage.id, props.privateMessage.notifyPersonId, props.context.person.id]);
59
+
60
+ return (
61
+ <Paper elevation={0} sx={{
62
+ height: '100%',
63
+ display: 'flex',
64
+ flexDirection: 'column',
65
+ overflow: 'hidden',
66
+ position: 'relative'
67
+ }}>
68
+ {/* Fixed Header - Always visible */}
69
+ <Box sx={{
70
+ p: 2,
71
+ borderBottom: 1,
72
+ borderColor: 'divider',
73
+ flexShrink: 0,
74
+ backgroundColor: 'background.paper',
75
+ zIndex: 1
76
+ }}>
77
+ <Stack direction="row" alignItems="center" spacing={2}>
78
+ <IconButton onClick={props.onBack}>
79
+ <ArrowBackIcon />
80
+ </IconButton>
81
+ <Stack direction="row" spacing={2} alignItems="center" sx={{ flex: 1 }}>
82
+ <PersonAvatar person={props.privateMessage.person} size="small" />
83
+ <Box>
84
+ <Typography variant="h6" component="h2">
85
+ {props.privateMessage.person.name.display}
86
+ </Typography>
87
+ <Typography variant="caption" color="textSecondary">
88
+ {Locale.label("wrapper.privateConversation", "Private Conversation")}
89
+ </Typography>
90
+ </Box>
91
+ </Stack>
92
+ </Stack>
93
+ </Box>
94
+
95
+ {/* Chat area - Scrollable content */}
96
+ <Box sx={{
97
+ flex: 1,
98
+ display: 'flex',
99
+ flexDirection: 'column',
100
+ minHeight: 0,
101
+ overflow: 'hidden'
102
+ }}>
103
+ <Notes
104
+ maxHeight="100%"
105
+ context={props.context}
106
+ conversationId={props.privateMessage.conversationId}
107
+ noDisplayBox={true}
108
+ refreshKey={props.refreshKey}
109
+ />
110
+ </Box>
111
+ </Paper>
112
+ );
113
+ };
27
114