@churchapps/apphelper 0.5.1 → 0.5.3
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.
- package/dist/components/Loading.d.ts +1 -0
- package/dist/components/Loading.d.ts.map +1 -1
- package/dist/components/Loading.js +1 -45
- package/dist/components/Loading.js.map +1 -1
- package/dist/components/notes/Notes.js +27 -27
- package/dist/helpers/AnalyticsHelper.d.ts.map +1 -1
- package/dist/helpers/AnalyticsHelper.js +21 -6
- package/dist/helpers/AnalyticsHelper.js.map +1 -1
- package/dist/public/css/cropper.css +309 -309
- package/dist/public/css/styles.css +111 -111
- package/dist/public/locales/de.json +270 -0
- package/dist/public/locales/en.json +277 -0
- package/dist/public/locales/es.json +272 -0
- package/dist/public/locales/fr.json +270 -0
- package/dist/public/locales/hi.json +270 -0
- package/dist/public/locales/it.json +270 -0
- package/dist/public/locales/ko.json +270 -0
- package/dist/public/locales/no.json +270 -0
- package/dist/public/locales/pt.json +270 -0
- package/dist/public/locales/ru.json +270 -0
- package/dist/public/locales/tl.json +270 -0
- package/dist/public/locales/zh.json +270 -0
- package/package.json +72 -72
- package/public/css/cropper.css +309 -309
- package/public/css/styles.css +111 -111
- package/public/locales/de.json +269 -269
- package/public/locales/en.json +276 -276
- package/public/locales/es.json +272 -272
- package/public/locales/fr.json +269 -269
- package/public/locales/hi.json +269 -269
- package/public/locales/it.json +269 -269
- package/public/locales/ko.json +269 -269
- package/public/locales/no.json +269 -269
- package/public/locales/pt.json +269 -269
- package/public/locales/ru.json +269 -269
- package/public/locales/tl.json +269 -269
- package/public/locales/zh.json +269 -269
- package/src/components/DisplayBox.tsx +83 -83
- package/src/components/ErrorMessages.tsx +28 -28
- package/src/components/ExportLink.tsx +81 -81
- package/src/components/FloatingSupport.tsx +18 -18
- package/src/components/FormCardPayment.tsx +184 -184
- package/src/components/FormSubmissionEdit.tsx +168 -168
- package/src/components/HelpIcon.tsx +12 -12
- package/src/components/ImageEditor.tsx +161 -161
- package/src/components/InputBox.tsx +96 -96
- package/src/components/Loading.tsx +31 -77
- package/src/components/PageHeader.tsx +110 -110
- package/src/components/PersonAvatar.tsx +77 -77
- package/src/components/QuestionEdit.tsx +99 -99
- package/src/components/SmallButton.tsx +42 -42
- package/src/components/SupportModal.tsx +32 -32
- package/src/components/TabPanel.tsx +28 -28
- package/src/components/gallery/GalleryModal.tsx +173 -173
- package/src/components/gallery/StockPhotos.tsx +95 -95
- package/src/components/gallery/index.ts +1 -1
- package/src/components/header/Banner.tsx +11 -11
- package/src/components/header/PrimaryMenu.tsx +100 -100
- package/src/components/header/SecondaryMenu.tsx +23 -23
- package/src/components/header/SecondaryMenuAlt.tsx +40 -40
- package/src/components/header/SiteHeader.tsx +207 -207
- package/src/components/header/SupportDrawer.tsx +111 -111
- package/src/components/header/index.tsx +2 -2
- package/src/components/index.tsx +20 -20
- package/src/components/notes/AddNote.tsx +180 -180
- package/src/components/notes/Note.tsx +68 -68
- package/src/components/notes/Notes.tsx +208 -208
- package/src/components/notes/index.ts +3 -3
- package/src/components/wrapper/AppList.tsx +19 -19
- package/src/components/wrapper/ChurchList.tsx +154 -154
- package/src/components/wrapper/NavItem.tsx +47 -47
- package/src/components/wrapper/NewPrivateMessage.tsx +253 -253
- package/src/components/wrapper/Notifications.tsx +223 -223
- package/src/components/wrapper/PrivateMessageDetails.tsx +112 -112
- package/src/components/wrapper/PrivateMessages.tsx +576 -576
- package/src/components/wrapper/UserMenu.tsx +383 -383
- package/src/components/wrapper/index.tsx +8 -8
- package/src/helpers/AnalyticsHelper.ts +44 -32
- package/src/helpers/AppearanceHelper.ts +73 -73
- package/src/helpers/ArrayHelper.ts +87 -87
- package/src/helpers/CurrencyHelper.ts +10 -10
- package/src/helpers/DateHelper.ts +104 -104
- package/src/helpers/ErrorHelper.ts +43 -43
- package/src/helpers/EventHelper.ts +49 -49
- package/src/helpers/FileHelper.ts +31 -31
- package/src/helpers/Locale.ts +457 -457
- package/src/helpers/NotificationService.ts +296 -296
- package/src/helpers/PersonHelper.ts +62 -62
- package/src/helpers/SlugHelper.ts +37 -37
- package/src/helpers/SocketHelper.ts +296 -296
- package/src/helpers/UniqueIdHelper.ts +36 -36
- package/src/helpers/UserHelper.ts +107 -107
- package/src/helpers/createEmotionCache.ts +17 -17
- package/src/helpers/index.ts +58 -58
- package/src/hooks/index.ts +3 -3
- package/src/hooks/useMountedState.ts +16 -16
- package/src/hooks/useNotifications.ts +93 -93
- package/src/index.ts +2 -2
- package/src/types/interface-extensions.d.ts +11 -11
- package/tsconfig.json +31 -31
|
@@ -1,207 +1,207 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
import { AppBar, Link, styled, Toolbar } from "@mui/material";
|
|
3
|
-
import { UserMenu } from "../wrapper/UserMenu";
|
|
4
|
-
import { PersonHelper } from "../../helpers/PersonHelper";
|
|
5
|
-
import { PrimaryMenu } from "./PrimaryMenu";
|
|
6
|
-
import { SecondaryMenu } from "./SecondaryMenu";
|
|
7
|
-
import { SecondaryMenuAlt } from "./SecondaryMenuAlt";
|
|
8
|
-
import { SupportDrawer } from "./SupportDrawer";
|
|
9
|
-
import { UserContextInterface } from "@churchapps/helpers";
|
|
10
|
-
import { NotificationService } from "../../helpers/NotificationService";
|
|
11
|
-
|
|
12
|
-
type Props = {
|
|
13
|
-
primaryMenuLabel: string;
|
|
14
|
-
primaryMenuItems:{ url: string, icon:string, label: string }[];
|
|
15
|
-
secondaryMenuLabel: string;
|
|
16
|
-
secondaryMenuItems:{ url: string, label: string }[];
|
|
17
|
-
context: UserContextInterface;
|
|
18
|
-
appName: string;
|
|
19
|
-
onNavigate: (url: string) => void;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export const SiteHeader = React.memo((props:Props) => {
|
|
23
|
-
// Initialize NotificationService without subscribing to count changes to prevent re-renders
|
|
24
|
-
React.useEffect(() => {
|
|
25
|
-
const initializeNotifications = async () => {
|
|
26
|
-
if (props.context?.person?.id && props.context?.userChurch?.church?.id) {
|
|
27
|
-
const service = NotificationService.getInstance();
|
|
28
|
-
await service.initialize(props.context);
|
|
29
|
-
}
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
initializeNotifications();
|
|
33
|
-
}, [props.context?.person?.id, props.context?.userChurch?.church?.id]);
|
|
34
|
-
|
|
35
|
-
const refresh = React.useCallback(async () => {
|
|
36
|
-
// Direct access to NotificationService for refresh functionality
|
|
37
|
-
await NotificationService.getInstance().refresh();
|
|
38
|
-
}, []);
|
|
39
|
-
|
|
40
|
-
// Memoize userName to prevent recreation
|
|
41
|
-
const userName = React.useMemo(() => {
|
|
42
|
-
if (props.context?.user) {
|
|
43
|
-
return `${props.context.user.firstName} ${props.context.user.lastName}`;
|
|
44
|
-
}
|
|
45
|
-
return '';
|
|
46
|
-
}, [props.context?.user?.firstName, props.context?.user?.lastName]);
|
|
47
|
-
|
|
48
|
-
// Memoize profilePicture URL
|
|
49
|
-
const profilePicture = React.useMemo(() => {
|
|
50
|
-
return PersonHelper.getPhotoUrl(props.context?.person);
|
|
51
|
-
}, [props.context?.person]);
|
|
52
|
-
|
|
53
|
-
// Create a stable context object to prevent UserMenu recreation
|
|
54
|
-
const stableContext = React.useMemo(() => {
|
|
55
|
-
console.log('SiteHeader - Creating stableContext');
|
|
56
|
-
console.log('SiteHeader - props.context:', props.context);
|
|
57
|
-
console.log('SiteHeader - props.context.userChurches:', props.context?.userChurches);
|
|
58
|
-
|
|
59
|
-
if (!props.context) return undefined;
|
|
60
|
-
|
|
61
|
-
return {
|
|
62
|
-
user: props.context.user,
|
|
63
|
-
person: props.context.person,
|
|
64
|
-
userChurch: props.context.userChurch,
|
|
65
|
-
userChurches: props.context.userChurches,
|
|
66
|
-
setUser: props.context.setUser,
|
|
67
|
-
setPerson: props.context.setPerson,
|
|
68
|
-
setUserChurch: props.context.setUserChurch,
|
|
69
|
-
setUserChurches: props.context.setUserChurches
|
|
70
|
-
};
|
|
71
|
-
}, [
|
|
72
|
-
props.context?.user?.id,
|
|
73
|
-
props.context?.user?.firstName,
|
|
74
|
-
props.context?.user?.lastName,
|
|
75
|
-
props.context?.person?.id,
|
|
76
|
-
props.context?.userChurch?.church?.id,
|
|
77
|
-
props.context?.userChurches,
|
|
78
|
-
props.context?.setUser,
|
|
79
|
-
props.context?.setPerson,
|
|
80
|
-
props.context?.setUserChurch,
|
|
81
|
-
props.context?.setUserChurches
|
|
82
|
-
]);
|
|
83
|
-
|
|
84
|
-
const CustomAppBar = styled(AppBar)(
|
|
85
|
-
({ theme }) => ({
|
|
86
|
-
zIndex: theme.zIndex.drawer + 1,
|
|
87
|
-
backgroundColor: "var(--c1, #1565C0)",
|
|
88
|
-
transition: theme.transitions.create(["width", "margin"], {
|
|
89
|
-
easing: theme.transitions.easing.sharp,
|
|
90
|
-
duration: theme.transitions.duration.leavingScreen
|
|
91
|
-
}),
|
|
92
|
-
"& .MuiIcon-root": { color: "#FFFFFF" }
|
|
93
|
-
})
|
|
94
|
-
);
|
|
95
|
-
|
|
96
|
-
const getRelatedArticles = () => {
|
|
97
|
-
let result: any [] = [];
|
|
98
|
-
if (props.appName === "CHUMS") {
|
|
99
|
-
if (props.primaryMenuLabel === "People") {
|
|
100
|
-
if (props.secondaryMenuLabel === "People") result = ["chums/adding-people", "chums/advanced-search", "chums/assigning-roles"];
|
|
101
|
-
else if (props.secondaryMenuLabel === "Groups") result = ["chums/group-roster", "chums/groups", "chums/group-calendar"];
|
|
102
|
-
else if (props.secondaryMenuLabel === "Attendance") result = ["chums/attendance", "chums/checkin"];
|
|
103
|
-
}
|
|
104
|
-
else if (props.primaryMenuLabel === "Donations") {
|
|
105
|
-
if (props.secondaryMenuLabel === "Summary") result = ["chums/donation-report"];
|
|
106
|
-
else if (props.secondaryMenuLabel === "Batches" || props.secondaryMenuLabel === "Funds") result = ["chums/giving", "chums/manual-input"];
|
|
107
|
-
}
|
|
108
|
-
else if (props.primaryMenuLabel === "Serving") {
|
|
109
|
-
if (props.secondaryMenuLabel === "Plans") result = ["chums/plans"];
|
|
110
|
-
else if (props.secondaryMenuLabel === "Tasks") result = ["chums/tasks", "chums/automations"];
|
|
111
|
-
}
|
|
112
|
-
else if (props.primaryMenuLabel === "Settings") {
|
|
113
|
-
if (props.secondaryMenuLabel === "Settings") result = ["chums/assigning-roles", "chums/exporting-data", "chums/import-csv", "chums/import-from-breeze"];
|
|
114
|
-
else if (props.secondaryMenuLabel === "Forms") result = ["chums/forms"];
|
|
115
|
-
}
|
|
116
|
-
} else if (props.appName === "B1") {
|
|
117
|
-
if (props.primaryMenuLabel === "Mobile App") result = ["b1/admin/portal", "b1/mobile/setup"];
|
|
118
|
-
else if (props.primaryMenuLabel === "Website") result = ["b1/admin/portal", "b1/admin/website-elements", "b1/admin/website-setup"];
|
|
119
|
-
else if (props.primaryMenuLabel === "Sermons") result = ["b1/admin/sermons", "b1/admin/stream-setup"];
|
|
120
|
-
else if (props.primaryMenuLabel === "Calendars") result = ["b1/portal/calendars"];
|
|
121
|
-
}
|
|
122
|
-
return result;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
/*<Typography variant="h6" noWrap>{UserHelper.currentUserChurch?.church?.name || ""}</Typography>*/
|
|
126
|
-
return (<>
|
|
127
|
-
<div id="site-header" style={{
|
|
128
|
-
'--c1': '#1565C0',
|
|
129
|
-
'--c1d1': '#1358AD',
|
|
130
|
-
'--c1d2': '#114A99',
|
|
131
|
-
'--c1l2': '#568BDA',
|
|
132
|
-
backgroundColor:"var(--c1)",
|
|
133
|
-
color: "#FFF"
|
|
134
|
-
} as React.CSSProperties}>
|
|
135
|
-
<CustomAppBar id="site-app-bar" position="absolute">
|
|
136
|
-
<Toolbar id="site-toolbar" sx={{ pr: "24px", backgroundColor: "var(--c1)", minHeight: "64px !important" }}>
|
|
137
|
-
<PrimaryMenu label={props.primaryMenuLabel} menuItems={props.primaryMenuItems} onNavigate={props.onNavigate} />
|
|
138
|
-
<SecondaryMenu label={props.secondaryMenuLabel} menuItems={props.secondaryMenuItems} onNavigate={props.onNavigate} />
|
|
139
|
-
<div id="secondary-menu-container" style={{ flex: 1 }}>
|
|
140
|
-
<SecondaryMenuAlt label={props.secondaryMenuLabel} menuItems={props.secondaryMenuItems} onNavigate={props.onNavigate} />
|
|
141
|
-
</div>
|
|
142
|
-
{props.context?.user?.id && (
|
|
143
|
-
<UserMenu
|
|
144
|
-
key="user-menu-stable"
|
|
145
|
-
profilePicture={profilePicture}
|
|
146
|
-
userName={userName}
|
|
147
|
-
context={stableContext}
|
|
148
|
-
appName={props.appName}
|
|
149
|
-
loadCounts={refresh}
|
|
150
|
-
notificationCounts={{notificationCount: 0, pmCount: 0}}
|
|
151
|
-
onNavigate={props.onNavigate}
|
|
152
|
-
/>
|
|
153
|
-
)}
|
|
154
|
-
{!props.context?.user?.id && <Link id="login-link" href="/login" color="inherit" style={{ textDecoration: "none" }}>Login</Link>}
|
|
155
|
-
<SupportDrawer appName={props.appName} relatedArticles={getRelatedArticles()} />
|
|
156
|
-
</Toolbar>
|
|
157
|
-
</CustomAppBar>
|
|
158
|
-
<div id="app-bar-spacer" style={{ height: '64px' }}></div>
|
|
159
|
-
</div>
|
|
160
|
-
</>
|
|
161
|
-
);
|
|
162
|
-
}, (prevProps, nextProps) => {
|
|
163
|
-
// Custom comparison to prevent unnecessary re-renders
|
|
164
|
-
|
|
165
|
-
// Check if essential props have changed
|
|
166
|
-
if (prevProps.primaryMenuLabel !== nextProps.primaryMenuLabel ||
|
|
167
|
-
prevProps.secondaryMenuLabel !== nextProps.secondaryMenuLabel ||
|
|
168
|
-
prevProps.appName !== nextProps.appName) {
|
|
169
|
-
return false;
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
// Check if menu items arrays have changed (shallow comparison)
|
|
173
|
-
if (prevProps.primaryMenuItems?.length !== nextProps.primaryMenuItems?.length ||
|
|
174
|
-
prevProps.secondaryMenuItems?.length !== nextProps.secondaryMenuItems?.length) {
|
|
175
|
-
return false;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
// Check if user context has actually changed (deep comparison of essential parts)
|
|
179
|
-
const prevUser = prevProps.context?.user;
|
|
180
|
-
const nextUser = nextProps.context?.user;
|
|
181
|
-
|
|
182
|
-
if (prevUser?.id !== nextUser?.id ||
|
|
183
|
-
prevUser?.firstName !== nextUser?.firstName ||
|
|
184
|
-
prevUser?.lastName !== nextUser?.lastName) {
|
|
185
|
-
return false;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
// Check if person context has changed
|
|
189
|
-
if (prevProps.context?.person?.id !== nextProps.context?.person?.id) {
|
|
190
|
-
return false;
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
// Check if church context has changed
|
|
194
|
-
if (prevProps.context?.userChurch?.church?.id !== nextProps.context?.userChurch?.church?.id) {
|
|
195
|
-
return false;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
// Check if onNavigate function reference has changed
|
|
199
|
-
if (prevProps.onNavigate !== nextProps.onNavigate) {
|
|
200
|
-
return false;
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
// All essential props are the same, skip re-render
|
|
204
|
-
return true;
|
|
205
|
-
});
|
|
206
|
-
|
|
207
|
-
SiteHeader.displayName = 'SiteHeader';
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { AppBar, Link, styled, Toolbar } from "@mui/material";
|
|
3
|
+
import { UserMenu } from "../wrapper/UserMenu";
|
|
4
|
+
import { PersonHelper } from "../../helpers/PersonHelper";
|
|
5
|
+
import { PrimaryMenu } from "./PrimaryMenu";
|
|
6
|
+
import { SecondaryMenu } from "./SecondaryMenu";
|
|
7
|
+
import { SecondaryMenuAlt } from "./SecondaryMenuAlt";
|
|
8
|
+
import { SupportDrawer } from "./SupportDrawer";
|
|
9
|
+
import { UserContextInterface } from "@churchapps/helpers";
|
|
10
|
+
import { NotificationService } from "../../helpers/NotificationService";
|
|
11
|
+
|
|
12
|
+
type Props = {
|
|
13
|
+
primaryMenuLabel: string;
|
|
14
|
+
primaryMenuItems:{ url: string, icon:string, label: string }[];
|
|
15
|
+
secondaryMenuLabel: string;
|
|
16
|
+
secondaryMenuItems:{ url: string, label: string }[];
|
|
17
|
+
context: UserContextInterface;
|
|
18
|
+
appName: string;
|
|
19
|
+
onNavigate: (url: string) => void;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export const SiteHeader = React.memo((props:Props) => {
|
|
23
|
+
// Initialize NotificationService without subscribing to count changes to prevent re-renders
|
|
24
|
+
React.useEffect(() => {
|
|
25
|
+
const initializeNotifications = async () => {
|
|
26
|
+
if (props.context?.person?.id && props.context?.userChurch?.church?.id) {
|
|
27
|
+
const service = NotificationService.getInstance();
|
|
28
|
+
await service.initialize(props.context);
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
initializeNotifications();
|
|
33
|
+
}, [props.context?.person?.id, props.context?.userChurch?.church?.id]);
|
|
34
|
+
|
|
35
|
+
const refresh = React.useCallback(async () => {
|
|
36
|
+
// Direct access to NotificationService for refresh functionality
|
|
37
|
+
await NotificationService.getInstance().refresh();
|
|
38
|
+
}, []);
|
|
39
|
+
|
|
40
|
+
// Memoize userName to prevent recreation
|
|
41
|
+
const userName = React.useMemo(() => {
|
|
42
|
+
if (props.context?.user) {
|
|
43
|
+
return `${props.context.user.firstName} ${props.context.user.lastName}`;
|
|
44
|
+
}
|
|
45
|
+
return '';
|
|
46
|
+
}, [props.context?.user?.firstName, props.context?.user?.lastName]);
|
|
47
|
+
|
|
48
|
+
// Memoize profilePicture URL
|
|
49
|
+
const profilePicture = React.useMemo(() => {
|
|
50
|
+
return PersonHelper.getPhotoUrl(props.context?.person);
|
|
51
|
+
}, [props.context?.person]);
|
|
52
|
+
|
|
53
|
+
// Create a stable context object to prevent UserMenu recreation
|
|
54
|
+
const stableContext = React.useMemo(() => {
|
|
55
|
+
console.log('SiteHeader - Creating stableContext');
|
|
56
|
+
console.log('SiteHeader - props.context:', props.context);
|
|
57
|
+
console.log('SiteHeader - props.context.userChurches:', props.context?.userChurches);
|
|
58
|
+
|
|
59
|
+
if (!props.context) return undefined;
|
|
60
|
+
|
|
61
|
+
return {
|
|
62
|
+
user: props.context.user,
|
|
63
|
+
person: props.context.person,
|
|
64
|
+
userChurch: props.context.userChurch,
|
|
65
|
+
userChurches: props.context.userChurches,
|
|
66
|
+
setUser: props.context.setUser,
|
|
67
|
+
setPerson: props.context.setPerson,
|
|
68
|
+
setUserChurch: props.context.setUserChurch,
|
|
69
|
+
setUserChurches: props.context.setUserChurches
|
|
70
|
+
};
|
|
71
|
+
}, [
|
|
72
|
+
props.context?.user?.id,
|
|
73
|
+
props.context?.user?.firstName,
|
|
74
|
+
props.context?.user?.lastName,
|
|
75
|
+
props.context?.person?.id,
|
|
76
|
+
props.context?.userChurch?.church?.id,
|
|
77
|
+
props.context?.userChurches,
|
|
78
|
+
props.context?.setUser,
|
|
79
|
+
props.context?.setPerson,
|
|
80
|
+
props.context?.setUserChurch,
|
|
81
|
+
props.context?.setUserChurches
|
|
82
|
+
]);
|
|
83
|
+
|
|
84
|
+
const CustomAppBar = styled(AppBar)(
|
|
85
|
+
({ theme }) => ({
|
|
86
|
+
zIndex: theme.zIndex.drawer + 1,
|
|
87
|
+
backgroundColor: "var(--c1, #1565C0)",
|
|
88
|
+
transition: theme.transitions.create(["width", "margin"], {
|
|
89
|
+
easing: theme.transitions.easing.sharp,
|
|
90
|
+
duration: theme.transitions.duration.leavingScreen
|
|
91
|
+
}),
|
|
92
|
+
"& .MuiIcon-root": { color: "#FFFFFF" }
|
|
93
|
+
})
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
const getRelatedArticles = () => {
|
|
97
|
+
let result: any [] = [];
|
|
98
|
+
if (props.appName === "CHUMS") {
|
|
99
|
+
if (props.primaryMenuLabel === "People") {
|
|
100
|
+
if (props.secondaryMenuLabel === "People") result = ["chums/adding-people", "chums/advanced-search", "chums/assigning-roles"];
|
|
101
|
+
else if (props.secondaryMenuLabel === "Groups") result = ["chums/group-roster", "chums/groups", "chums/group-calendar"];
|
|
102
|
+
else if (props.secondaryMenuLabel === "Attendance") result = ["chums/attendance", "chums/checkin"];
|
|
103
|
+
}
|
|
104
|
+
else if (props.primaryMenuLabel === "Donations") {
|
|
105
|
+
if (props.secondaryMenuLabel === "Summary") result = ["chums/donation-report"];
|
|
106
|
+
else if (props.secondaryMenuLabel === "Batches" || props.secondaryMenuLabel === "Funds") result = ["chums/giving", "chums/manual-input"];
|
|
107
|
+
}
|
|
108
|
+
else if (props.primaryMenuLabel === "Serving") {
|
|
109
|
+
if (props.secondaryMenuLabel === "Plans") result = ["chums/plans"];
|
|
110
|
+
else if (props.secondaryMenuLabel === "Tasks") result = ["chums/tasks", "chums/automations"];
|
|
111
|
+
}
|
|
112
|
+
else if (props.primaryMenuLabel === "Settings") {
|
|
113
|
+
if (props.secondaryMenuLabel === "Settings") result = ["chums/assigning-roles", "chums/exporting-data", "chums/import-csv", "chums/import-from-breeze"];
|
|
114
|
+
else if (props.secondaryMenuLabel === "Forms") result = ["chums/forms"];
|
|
115
|
+
}
|
|
116
|
+
} else if (props.appName === "B1") {
|
|
117
|
+
if (props.primaryMenuLabel === "Mobile App") result = ["b1/admin/portal", "b1/mobile/setup"];
|
|
118
|
+
else if (props.primaryMenuLabel === "Website") result = ["b1/admin/portal", "b1/admin/website-elements", "b1/admin/website-setup"];
|
|
119
|
+
else if (props.primaryMenuLabel === "Sermons") result = ["b1/admin/sermons", "b1/admin/stream-setup"];
|
|
120
|
+
else if (props.primaryMenuLabel === "Calendars") result = ["b1/portal/calendars"];
|
|
121
|
+
}
|
|
122
|
+
return result;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/*<Typography variant="h6" noWrap>{UserHelper.currentUserChurch?.church?.name || ""}</Typography>*/
|
|
126
|
+
return (<>
|
|
127
|
+
<div id="site-header" style={{
|
|
128
|
+
'--c1': '#1565C0',
|
|
129
|
+
'--c1d1': '#1358AD',
|
|
130
|
+
'--c1d2': '#114A99',
|
|
131
|
+
'--c1l2': '#568BDA',
|
|
132
|
+
backgroundColor:"var(--c1)",
|
|
133
|
+
color: "#FFF"
|
|
134
|
+
} as React.CSSProperties}>
|
|
135
|
+
<CustomAppBar id="site-app-bar" position="absolute">
|
|
136
|
+
<Toolbar id="site-toolbar" sx={{ pr: "24px", backgroundColor: "var(--c1)", minHeight: "64px !important" }}>
|
|
137
|
+
<PrimaryMenu label={props.primaryMenuLabel} menuItems={props.primaryMenuItems} onNavigate={props.onNavigate} />
|
|
138
|
+
<SecondaryMenu label={props.secondaryMenuLabel} menuItems={props.secondaryMenuItems} onNavigate={props.onNavigate} />
|
|
139
|
+
<div id="secondary-menu-container" style={{ flex: 1 }}>
|
|
140
|
+
<SecondaryMenuAlt label={props.secondaryMenuLabel} menuItems={props.secondaryMenuItems} onNavigate={props.onNavigate} />
|
|
141
|
+
</div>
|
|
142
|
+
{props.context?.user?.id && (
|
|
143
|
+
<UserMenu
|
|
144
|
+
key="user-menu-stable"
|
|
145
|
+
profilePicture={profilePicture}
|
|
146
|
+
userName={userName}
|
|
147
|
+
context={stableContext}
|
|
148
|
+
appName={props.appName}
|
|
149
|
+
loadCounts={refresh}
|
|
150
|
+
notificationCounts={{notificationCount: 0, pmCount: 0}}
|
|
151
|
+
onNavigate={props.onNavigate}
|
|
152
|
+
/>
|
|
153
|
+
)}
|
|
154
|
+
{!props.context?.user?.id && <Link id="login-link" href="/login" color="inherit" style={{ textDecoration: "none" }}>Login</Link>}
|
|
155
|
+
<SupportDrawer appName={props.appName} relatedArticles={getRelatedArticles()} />
|
|
156
|
+
</Toolbar>
|
|
157
|
+
</CustomAppBar>
|
|
158
|
+
<div id="app-bar-spacer" style={{ height: '64px' }}></div>
|
|
159
|
+
</div>
|
|
160
|
+
</>
|
|
161
|
+
);
|
|
162
|
+
}, (prevProps, nextProps) => {
|
|
163
|
+
// Custom comparison to prevent unnecessary re-renders
|
|
164
|
+
|
|
165
|
+
// Check if essential props have changed
|
|
166
|
+
if (prevProps.primaryMenuLabel !== nextProps.primaryMenuLabel ||
|
|
167
|
+
prevProps.secondaryMenuLabel !== nextProps.secondaryMenuLabel ||
|
|
168
|
+
prevProps.appName !== nextProps.appName) {
|
|
169
|
+
return false;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Check if menu items arrays have changed (shallow comparison)
|
|
173
|
+
if (prevProps.primaryMenuItems?.length !== nextProps.primaryMenuItems?.length ||
|
|
174
|
+
prevProps.secondaryMenuItems?.length !== nextProps.secondaryMenuItems?.length) {
|
|
175
|
+
return false;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Check if user context has actually changed (deep comparison of essential parts)
|
|
179
|
+
const prevUser = prevProps.context?.user;
|
|
180
|
+
const nextUser = nextProps.context?.user;
|
|
181
|
+
|
|
182
|
+
if (prevUser?.id !== nextUser?.id ||
|
|
183
|
+
prevUser?.firstName !== nextUser?.firstName ||
|
|
184
|
+
prevUser?.lastName !== nextUser?.lastName) {
|
|
185
|
+
return false;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Check if person context has changed
|
|
189
|
+
if (prevProps.context?.person?.id !== nextProps.context?.person?.id) {
|
|
190
|
+
return false;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Check if church context has changed
|
|
194
|
+
if (prevProps.context?.userChurch?.church?.id !== nextProps.context?.userChurch?.church?.id) {
|
|
195
|
+
return false;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// Check if onNavigate function reference has changed
|
|
199
|
+
if (prevProps.onNavigate !== nextProps.onNavigate) {
|
|
200
|
+
return false;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// All essential props are the same, skip re-render
|
|
204
|
+
return true;
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
SiteHeader.displayName = 'SiteHeader';
|
|
@@ -1,111 +1,111 @@
|
|
|
1
|
-
import React, { useEffect, useState } from "react";
|
|
2
|
-
import { Avatar, Box, Button, Drawer, Icon, IconButton, List, ListItem, ListItemButton, ListItemIcon, ListItemText, Typography } from "@mui/material";
|
|
3
|
-
import { ApiHelper, UserHelper } from "../../helpers";
|
|
4
|
-
|
|
5
|
-
type Props = {
|
|
6
|
-
appName: string;
|
|
7
|
-
relatedArticles?: string[];
|
|
8
|
-
};
|
|
9
|
-
|
|
10
|
-
export const SupportDrawer = (props: Props) => {
|
|
11
|
-
const [open, setOpen] = useState<boolean>(false);
|
|
12
|
-
const [supportContact, setSupportContact] = useState<string>(null);
|
|
13
|
-
const [churchLogo, setChurchLogo] = useState<string>(null);
|
|
14
|
-
const supportHref = "https://support.churchapps.org/";
|
|
15
|
-
|
|
16
|
-
let currentAppName = "";
|
|
17
|
-
if (props.appName === "CHUMS") currentAppName = "chums";
|
|
18
|
-
if (props.appName === "B1") currentAppName = "b1";
|
|
19
|
-
|
|
20
|
-
const validateEmail = (email: string) => {
|
|
21
|
-
return email.match(
|
|
22
|
-
/^(([^<>()[\]\\.,;:\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,}))$/
|
|
23
|
-
);
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
const handleChurchSupportClick = () => {
|
|
27
|
-
if (validateEmail(supportContact)) {
|
|
28
|
-
window.location.href = `mailto:${supportContact}`;
|
|
29
|
-
} else {
|
|
30
|
-
window.open(`http://${supportContact}`, "_blank").focus();
|
|
31
|
-
}
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
const loadData = () => {
|
|
35
|
-
if (UserHelper?.currentUserChurch?.church?.id) {
|
|
36
|
-
ApiHelper.get("/settings/public/" + UserHelper.currentUserChurch.church.id, "MembershipApi").then((data: any) => {
|
|
37
|
-
const contactRes = data?.supportContact;
|
|
38
|
-
if (contactRes && contactRes !== "") setSupportContact(contactRes);
|
|
39
|
-
|
|
40
|
-
const logoRes = data?.favicon_16x16;
|
|
41
|
-
if (logoRes && logoRes !== "") setChurchLogo(logoRes);
|
|
42
|
-
});
|
|
43
|
-
}
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
useEffect(loadData, []);
|
|
47
|
-
|
|
48
|
-
return (
|
|
49
|
-
<>
|
|
50
|
-
<IconButton sx={{ color: "white !important", borderRadius: 2 }} onClick={() => setOpen(true)} aria-label="Open support help" data-testid="support-help-button">
|
|
51
|
-
<Icon sx={{ fontSize: "26px !important" }}>help</Icon>
|
|
52
|
-
</IconButton>
|
|
53
|
-
<Drawer open={open} onClose={() => setOpen(false)} anchor="right" PaperProps={{ sx: { width: { xs: "80%", sm: "40%", md: "30%", lg: "20%" } } }}>
|
|
54
|
-
<Box
|
|
55
|
-
sx={{ paddingTop: 10, display: "flex", flexDirection: "column", justifyContent: "space-between", minHeight: "98vh" }}>
|
|
56
|
-
<Box>
|
|
57
|
-
{supportContact ? (
|
|
58
|
-
<Box sx={{ display: "flex", justifyContent: "end", alignItems: "center" }}>
|
|
59
|
-
<Box sx={{ cursor: "pointer", display: "flex", justifyContent: "center", alignItems: "center", marginRight: 2, borderRadius: 2, padding: "7px", backgroundColor: "#e9e9e9" }} component="div" onClick={() => handleChurchSupportClick()}>
|
|
60
|
-
<Avatar variant="rounded" src={churchLogo ? churchLogo : null} sx={{ marginRight: 1, bgcolor: "#568bda", width: 25, height: 25 }}>
|
|
61
|
-
<Icon fontSize="small">church</Icon>
|
|
62
|
-
</Avatar>
|
|
63
|
-
<Typography sx={{ color: "#568bda", fontSize: "13px" }}>Support</Typography>
|
|
64
|
-
</Box>
|
|
65
|
-
</Box>
|
|
66
|
-
) : null}
|
|
67
|
-
<List>
|
|
68
|
-
<ListItem disablePadding>
|
|
69
|
-
<ListItemButton href={supportHref + currentAppName} target="_blank" rel="noopener noreferrer">
|
|
70
|
-
<ListItemIcon sx={{ minWidth: "38px" }}><Icon sx={{ color: "#568bda" }}>article</Icon></ListItemIcon>
|
|
71
|
-
<ListItemText sx={{ color: "#568bda" }}>View Documentation</ListItemText>
|
|
72
|
-
</ListItemButton>
|
|
73
|
-
</ListItem>
|
|
74
|
-
</List>
|
|
75
|
-
{props?.relatedArticles?.length > 0 ? (
|
|
76
|
-
<Box sx={{ marginTop: 2 }}>
|
|
77
|
-
<Typography sx={{ color: "#568bda", textDecoration: "underline", textUnderlineOffset: 10, textDecorationThickness: 2, marginLeft: 2 }}>Features Tour</Typography>
|
|
78
|
-
<List sx={{ marginTop: 1.5 }}>
|
|
79
|
-
{props.relatedArticles.map((a) => {
|
|
80
|
-
const parts = a.split("/");
|
|
81
|
-
const lastPart = parts[parts.length - 1];
|
|
82
|
-
const result = lastPart.replace("-", " ");
|
|
83
|
-
return (
|
|
84
|
-
<ListItem disablePadding>
|
|
85
|
-
<ListItemButton href={supportHref + a} target="_blank" rel="noopener noreferrer">
|
|
86
|
-
<ListItemIcon sx={{ minWidth: "35px" }}><Icon sx={{ color: "#568bda" }}>play_circle</Icon></ListItemIcon>
|
|
87
|
-
<ListItemText><Typography sx={{ color: "#568bda", textTransform: "capitalize" }} fontSize="15px">{result}</Typography></ListItemText>
|
|
88
|
-
</ListItemButton>
|
|
89
|
-
</ListItem>
|
|
90
|
-
);
|
|
91
|
-
})}
|
|
92
|
-
</List>
|
|
93
|
-
</Box>
|
|
94
|
-
) : null}
|
|
95
|
-
</Box>
|
|
96
|
-
<Box sx={{ marginRight: 4, marginLeft: 4, marginTop: "auto" }}>
|
|
97
|
-
<hr style={{ color: "#568bda", borderColor: "#568bda", marginLeft: -13, marginRight: -13 }} />
|
|
98
|
-
<Box sx={{ display: "flex", alignItems: "center", justifyContent: "center", color: "#568bda", marginBottom: 1.5, marginTop: 1.5 }}>
|
|
99
|
-
<Avatar src="https://avatars.githubusercontent.com/u/74469593?s=200&v=4" variant="rounded" sx={{ width: 25, height: 25, marginRight: 1 }}>C</Avatar>
|
|
100
|
-
<Typography>ChurchApps</Typography>
|
|
101
|
-
</Box>
|
|
102
|
-
<Box sx={{ display: "flex", justifyContent: "space-between", alignItems: "center", gap: 4, marginBottom: 1 }}>
|
|
103
|
-
<Button variant="contained" size="small" startIcon={<Icon fontSize="small">mail</Icon>} fullWidth sx={{ backgroundColor: "#568bda" }} href="mailto:support@livecs.org">Support</Button>
|
|
104
|
-
<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>
|
|
105
|
-
</Box>
|
|
106
|
-
</Box>
|
|
107
|
-
</Box>
|
|
108
|
-
</Drawer>
|
|
109
|
-
</>
|
|
110
|
-
);
|
|
111
|
-
};
|
|
1
|
+
import React, { useEffect, useState } from "react";
|
|
2
|
+
import { Avatar, Box, Button, Drawer, Icon, IconButton, List, ListItem, ListItemButton, ListItemIcon, ListItemText, Typography } from "@mui/material";
|
|
3
|
+
import { ApiHelper, UserHelper } from "../../helpers";
|
|
4
|
+
|
|
5
|
+
type Props = {
|
|
6
|
+
appName: string;
|
|
7
|
+
relatedArticles?: string[];
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export const SupportDrawer = (props: Props) => {
|
|
11
|
+
const [open, setOpen] = useState<boolean>(false);
|
|
12
|
+
const [supportContact, setSupportContact] = useState<string>(null);
|
|
13
|
+
const [churchLogo, setChurchLogo] = useState<string>(null);
|
|
14
|
+
const supportHref = "https://support.churchapps.org/";
|
|
15
|
+
|
|
16
|
+
let currentAppName = "";
|
|
17
|
+
if (props.appName === "CHUMS") currentAppName = "chums";
|
|
18
|
+
if (props.appName === "B1") currentAppName = "b1";
|
|
19
|
+
|
|
20
|
+
const validateEmail = (email: string) => {
|
|
21
|
+
return email.match(
|
|
22
|
+
/^(([^<>()[\]\\.,;:\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,}))$/
|
|
23
|
+
);
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const handleChurchSupportClick = () => {
|
|
27
|
+
if (validateEmail(supportContact)) {
|
|
28
|
+
window.location.href = `mailto:${supportContact}`;
|
|
29
|
+
} else {
|
|
30
|
+
window.open(`http://${supportContact}`, "_blank").focus();
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const loadData = () => {
|
|
35
|
+
if (UserHelper?.currentUserChurch?.church?.id) {
|
|
36
|
+
ApiHelper.get("/settings/public/" + UserHelper.currentUserChurch.church.id, "MembershipApi").then((data: any) => {
|
|
37
|
+
const contactRes = data?.supportContact;
|
|
38
|
+
if (contactRes && contactRes !== "") setSupportContact(contactRes);
|
|
39
|
+
|
|
40
|
+
const logoRes = data?.favicon_16x16;
|
|
41
|
+
if (logoRes && logoRes !== "") setChurchLogo(logoRes);
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
useEffect(loadData, []);
|
|
47
|
+
|
|
48
|
+
return (
|
|
49
|
+
<>
|
|
50
|
+
<IconButton sx={{ color: "white !important", borderRadius: 2 }} onClick={() => setOpen(true)} aria-label="Open support help" data-testid="support-help-button">
|
|
51
|
+
<Icon sx={{ fontSize: "26px !important" }}>help</Icon>
|
|
52
|
+
</IconButton>
|
|
53
|
+
<Drawer open={open} onClose={() => setOpen(false)} anchor="right" PaperProps={{ sx: { width: { xs: "80%", sm: "40%", md: "30%", lg: "20%" } } }}>
|
|
54
|
+
<Box
|
|
55
|
+
sx={{ paddingTop: 10, display: "flex", flexDirection: "column", justifyContent: "space-between", minHeight: "98vh" }}>
|
|
56
|
+
<Box>
|
|
57
|
+
{supportContact ? (
|
|
58
|
+
<Box sx={{ display: "flex", justifyContent: "end", alignItems: "center" }}>
|
|
59
|
+
<Box sx={{ cursor: "pointer", display: "flex", justifyContent: "center", alignItems: "center", marginRight: 2, borderRadius: 2, padding: "7px", backgroundColor: "#e9e9e9" }} component="div" onClick={() => handleChurchSupportClick()}>
|
|
60
|
+
<Avatar variant="rounded" src={churchLogo ? churchLogo : null} sx={{ marginRight: 1, bgcolor: "#568bda", width: 25, height: 25 }}>
|
|
61
|
+
<Icon fontSize="small">church</Icon>
|
|
62
|
+
</Avatar>
|
|
63
|
+
<Typography sx={{ color: "#568bda", fontSize: "13px" }}>Support</Typography>
|
|
64
|
+
</Box>
|
|
65
|
+
</Box>
|
|
66
|
+
) : null}
|
|
67
|
+
<List>
|
|
68
|
+
<ListItem disablePadding>
|
|
69
|
+
<ListItemButton href={supportHref + currentAppName} target="_blank" rel="noopener noreferrer">
|
|
70
|
+
<ListItemIcon sx={{ minWidth: "38px" }}><Icon sx={{ color: "#568bda" }}>article</Icon></ListItemIcon>
|
|
71
|
+
<ListItemText sx={{ color: "#568bda" }}>View Documentation</ListItemText>
|
|
72
|
+
</ListItemButton>
|
|
73
|
+
</ListItem>
|
|
74
|
+
</List>
|
|
75
|
+
{props?.relatedArticles?.length > 0 ? (
|
|
76
|
+
<Box sx={{ marginTop: 2 }}>
|
|
77
|
+
<Typography sx={{ color: "#568bda", textDecoration: "underline", textUnderlineOffset: 10, textDecorationThickness: 2, marginLeft: 2 }}>Features Tour</Typography>
|
|
78
|
+
<List sx={{ marginTop: 1.5 }}>
|
|
79
|
+
{props.relatedArticles.map((a) => {
|
|
80
|
+
const parts = a.split("/");
|
|
81
|
+
const lastPart = parts[parts.length - 1];
|
|
82
|
+
const result = lastPart.replace("-", " ");
|
|
83
|
+
return (
|
|
84
|
+
<ListItem disablePadding>
|
|
85
|
+
<ListItemButton href={supportHref + a} target="_blank" rel="noopener noreferrer">
|
|
86
|
+
<ListItemIcon sx={{ minWidth: "35px" }}><Icon sx={{ color: "#568bda" }}>play_circle</Icon></ListItemIcon>
|
|
87
|
+
<ListItemText><Typography sx={{ color: "#568bda", textTransform: "capitalize" }} fontSize="15px">{result}</Typography></ListItemText>
|
|
88
|
+
</ListItemButton>
|
|
89
|
+
</ListItem>
|
|
90
|
+
);
|
|
91
|
+
})}
|
|
92
|
+
</List>
|
|
93
|
+
</Box>
|
|
94
|
+
) : null}
|
|
95
|
+
</Box>
|
|
96
|
+
<Box sx={{ marginRight: 4, marginLeft: 4, marginTop: "auto" }}>
|
|
97
|
+
<hr style={{ color: "#568bda", borderColor: "#568bda", marginLeft: -13, marginRight: -13 }} />
|
|
98
|
+
<Box sx={{ display: "flex", alignItems: "center", justifyContent: "center", color: "#568bda", marginBottom: 1.5, marginTop: 1.5 }}>
|
|
99
|
+
<Avatar src="https://avatars.githubusercontent.com/u/74469593?s=200&v=4" variant="rounded" sx={{ width: 25, height: 25, marginRight: 1 }}>C</Avatar>
|
|
100
|
+
<Typography>ChurchApps</Typography>
|
|
101
|
+
</Box>
|
|
102
|
+
<Box sx={{ display: "flex", justifyContent: "space-between", alignItems: "center", gap: 4, marginBottom: 1 }}>
|
|
103
|
+
<Button variant="contained" size="small" startIcon={<Icon fontSize="small">mail</Icon>} fullWidth sx={{ backgroundColor: "#568bda" }} href="mailto:support@livecs.org">Support</Button>
|
|
104
|
+
<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>
|
|
105
|
+
</Box>
|
|
106
|
+
</Box>
|
|
107
|
+
</Box>
|
|
108
|
+
</Drawer>
|
|
109
|
+
</>
|
|
110
|
+
);
|
|
111
|
+
};
|