@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.
- package/dist/components/DisplayBox.d.ts +1 -1
- package/dist/components/DisplayBox.d.ts.map +1 -1
- package/dist/components/DisplayBox.js +2 -4
- package/dist/components/DisplayBox.js.map +1 -1
- package/dist/components/ErrorMessages.d.ts.map +1 -1
- package/dist/components/ErrorMessages.js +1 -1
- package/dist/components/ErrorMessages.js.map +1 -1
- package/dist/components/ExportLink.d.ts.map +1 -1
- package/dist/components/ExportLink.js +10 -10
- package/dist/components/ExportLink.js.map +1 -1
- package/dist/components/FloatingSupport.js.map +1 -1
- package/dist/components/FormCardPayment.d.ts.map +1 -1
- package/dist/components/FormCardPayment.js +20 -5
- package/dist/components/FormCardPayment.js.map +1 -1
- package/dist/components/FormSubmissionEdit.d.ts.map +1 -1
- package/dist/components/FormSubmissionEdit.js +8 -10
- package/dist/components/FormSubmissionEdit.js.map +1 -1
- package/dist/components/HelpIcon.d.ts.map +1 -1
- package/dist/components/HelpIcon.js.map +1 -1
- package/dist/components/ImageEditor.d.ts.map +1 -1
- package/dist/components/ImageEditor.js +10 -10
- package/dist/components/ImageEditor.js.map +1 -1
- package/dist/components/InputBox.d.ts +2 -1
- package/dist/components/InputBox.d.ts.map +1 -1
- package/dist/components/InputBox.js +5 -7
- package/dist/components/InputBox.js.map +1 -1
- package/dist/components/Loading.d.ts.map +1 -1
- package/dist/components/Loading.js.map +1 -1
- package/dist/components/PageHeader.js +14 -14
- package/dist/components/PersonAvatar.d.ts.map +1 -1
- package/dist/components/PersonAvatar.js +9 -16
- package/dist/components/PersonAvatar.js.map +1 -1
- package/dist/components/QuestionEdit.js +3 -3
- package/dist/components/QuestionEdit.js.map +1 -1
- package/dist/components/SmallButton.d.ts.map +1 -1
- package/dist/components/SmallButton.js +2 -1
- package/dist/components/SmallButton.js.map +1 -1
- package/dist/components/SupportModal.js.map +1 -1
- package/dist/components/gallery/GalleryModal.js +10 -10
- package/dist/components/gallery/GalleryModal.js.map +1 -1
- package/dist/components/gallery/StockPhotos.js +12 -12
- package/dist/components/gallery/StockPhotos.js.map +1 -1
- package/dist/components/header/Banner.d.ts.map +1 -1
- package/dist/components/header/Banner.js.map +1 -1
- package/dist/components/header/PrimaryMenu.js +4 -4
- package/dist/components/header/PrimaryMenu.js.map +1 -1
- package/dist/components/header/SecondaryMenu.d.ts.map +1 -1
- package/dist/components/header/SecondaryMenu.js.map +1 -1
- package/dist/components/header/SecondaryMenuAlt.d.ts.map +1 -1
- package/dist/components/header/SecondaryMenuAlt.js.map +1 -1
- package/dist/components/header/SiteHeader.d.ts.map +1 -1
- package/dist/components/header/SiteHeader.js +7 -7
- package/dist/components/header/SiteHeader.js.map +1 -1
- package/dist/components/header/SupportDrawer.js +1 -1
- package/dist/components/header/SupportDrawer.js.map +1 -1
- package/dist/components/notes/AddNote.js +14 -14
- package/dist/components/notes/Note.d.ts.map +1 -1
- package/dist/components/notes/Note.js +7 -7
- package/dist/components/notes/Note.js.map +1 -1
- package/dist/components/notes/Notes.d.ts.map +1 -1
- package/dist/components/notes/Notes.js +4 -4
- package/dist/components/notes/Notes.js.map +1 -1
- package/dist/components/wrapper/AppList.d.ts.map +1 -1
- package/dist/components/wrapper/AppList.js.map +1 -1
- package/dist/components/wrapper/ChurchList.js +5 -5
- package/dist/components/wrapper/ChurchList.js.map +1 -1
- package/dist/components/wrapper/NavItem.js +1 -1
- package/dist/components/wrapper/NavItem.js.map +1 -1
- package/dist/components/wrapper/NewPrivateMessage.d.ts.map +1 -1
- package/dist/components/wrapper/NewPrivateMessage.js +7 -6
- package/dist/components/wrapper/NewPrivateMessage.js.map +1 -1
- package/dist/components/wrapper/Notifications.d.ts.map +1 -1
- package/dist/components/wrapper/Notifications.js +12 -14
- package/dist/components/wrapper/Notifications.js.map +1 -1
- package/dist/components/wrapper/PrivateMessageDetails.js +10 -10
- package/dist/components/wrapper/PrivateMessages.d.ts.map +1 -1
- package/dist/components/wrapper/PrivateMessages.js +119 -123
- package/dist/components/wrapper/PrivateMessages.js.map +1 -1
- package/dist/components/wrapper/UserMenu.d.ts.map +1 -1
- package/dist/components/wrapper/UserMenu.js +15 -15
- package/dist/components/wrapper/UserMenu.js.map +1 -1
- package/dist/helpers/AnalyticsHelper.d.ts.map +1 -1
- package/dist/helpers/AnalyticsHelper.js +3 -3
- package/dist/helpers/AnalyticsHelper.js.map +1 -1
- package/dist/helpers/ArrayHelper.d.ts.map +1 -1
- package/dist/helpers/ArrayHelper.js.map +1 -1
- package/dist/helpers/CurrencyHelper.d.ts +4 -0
- package/dist/helpers/CurrencyHelper.d.ts.map +1 -1
- package/dist/helpers/CurrencyHelper.js +65 -0
- package/dist/helpers/CurrencyHelper.js.map +1 -1
- package/dist/helpers/ErrorHelper.d.ts.map +1 -1
- package/dist/helpers/ErrorHelper.js.map +1 -1
- package/dist/helpers/EventHelper.d.ts.map +1 -1
- package/dist/helpers/EventHelper.js +1 -1
- package/dist/helpers/EventHelper.js.map +1 -1
- package/dist/helpers/FileHelper.d.ts.map +1 -1
- package/dist/helpers/FileHelper.js +3 -1
- package/dist/helpers/FileHelper.js.map +1 -1
- package/dist/helpers/Locale.d.ts.map +1 -1
- package/dist/helpers/Locale.js +10 -16
- package/dist/helpers/Locale.js.map +1 -1
- package/dist/helpers/NotificationService.js +4 -4
- package/dist/helpers/PersonHelper.d.ts.map +1 -1
- package/dist/helpers/PersonHelper.js +5 -4
- package/dist/helpers/PersonHelper.js.map +1 -1
- package/dist/helpers/SlugHelper.d.ts.map +1 -1
- package/dist/helpers/SlugHelper.js +5 -3
- package/dist/helpers/SlugHelper.js.map +1 -1
- package/dist/helpers/SocketHelper.d.ts.map +1 -1
- package/dist/helpers/SocketHelper.js +5 -10
- package/dist/helpers/SocketHelper.js.map +1 -1
- package/dist/helpers/UniqueIdHelper.d.ts.map +1 -1
- package/dist/helpers/UniqueIdHelper.js +132 -7
- package/dist/helpers/UniqueIdHelper.js.map +1 -1
- package/dist/helpers/UserHelper.d.ts.map +1 -1
- package/dist/helpers/UserHelper.js +2 -2
- package/dist/helpers/UserHelper.js.map +1 -1
- package/dist/hooks/useMountedState.d.ts.map +1 -1
- package/dist/hooks/useMountedState.js +1 -1
- package/dist/hooks/useMountedState.js.map +1 -1
- package/dist/hooks/useNotifications.d.ts +2 -2
- package/dist/hooks/useNotifications.js +5 -5
- package/dist/public/locales/de.json +15 -7
- package/dist/public/locales/es.json +193 -190
- package/dist/public/locales/fr.json +15 -7
- package/dist/public/locales/hi.json +15 -7
- package/dist/public/locales/it.json +15 -7
- package/dist/public/locales/ko.json +15 -7
- package/dist/public/locales/no.json +15 -7
- package/dist/public/locales/pt.json +15 -7
- package/dist/public/locales/ru.json +15 -7
- package/dist/public/locales/tl.json +15 -7
- package/dist/public/locales/zh.json +15 -7
- package/package.json +106 -73
- package/dist/helpers/DateHelper.d.ts +0 -18
- package/dist/helpers/DateHelper.d.ts.map +0 -1
- package/dist/helpers/DateHelper.js +0 -102
- package/dist/helpers/DateHelper.js.map +0 -1
- package/public/css/cropper.css +0 -309
- package/public/css/styles.css +0 -112
- package/public/locales/de.json +0 -273
- package/public/locales/en.json +0 -281
- package/public/locales/es.json +0 -278
- package/public/locales/fr.json +0 -273
- package/public/locales/hi.json +0 -273
- package/public/locales/it.json +0 -273
- package/public/locales/ko.json +0 -273
- package/public/locales/no.json +0 -273
- package/public/locales/pt.json +0 -273
- package/public/locales/ru.json +0 -273
- package/public/locales/tl.json +0 -273
- package/public/locales/zh.json +0 -273
- package/src/components/DisplayBox.tsx +0 -83
- package/src/components/ErrorMessages.tsx +0 -28
- package/src/components/ExportLink.tsx +0 -81
- package/src/components/FloatingSupport.tsx +0 -18
- package/src/components/FormCardPayment.tsx +0 -184
- package/src/components/FormSubmissionEdit.tsx +0 -168
- package/src/components/HelpIcon.tsx +0 -12
- package/src/components/ImageEditor.tsx +0 -167
- package/src/components/InputBox.tsx +0 -96
- package/src/components/Loading.tsx +0 -31
- package/src/components/PageHeader.tsx +0 -111
- package/src/components/PersonAvatar.tsx +0 -78
- package/src/components/QuestionEdit.tsx +0 -99
- package/src/components/SmallButton.tsx +0 -42
- package/src/components/SupportModal.tsx +0 -32
- package/src/components/TabPanel.tsx +0 -28
- package/src/components/gallery/GalleryModal.tsx +0 -170
- package/src/components/gallery/StockPhotos.tsx +0 -96
- package/src/components/gallery/index.ts +0 -2
- package/src/components/header/Banner.tsx +0 -11
- package/src/components/header/PrimaryMenu.tsx +0 -101
- package/src/components/header/SecondaryMenu.tsx +0 -23
- package/src/components/header/SecondaryMenuAlt.tsx +0 -40
- package/src/components/header/SiteHeader.tsx +0 -205
- package/src/components/header/SupportDrawer.tsx +0 -112
- package/src/components/header/index.tsx +0 -2
- package/src/components/index.tsx +0 -20
- package/src/components/notes/AddNote.tsx +0 -185
- package/src/components/notes/Note.tsx +0 -84
- package/src/components/notes/Notes.tsx +0 -208
- package/src/components/notes/index.ts +0 -3
- package/src/components/wrapper/AppList.tsx +0 -18
- package/src/components/wrapper/ChurchList.tsx +0 -145
- package/src/components/wrapper/NavItem.tsx +0 -47
- package/src/components/wrapper/NewPrivateMessage.tsx +0 -249
- package/src/components/wrapper/Notifications.tsx +0 -220
- package/src/components/wrapper/PrivateMessageDetails.tsx +0 -106
- package/src/components/wrapper/PrivateMessages.tsx +0 -574
- package/src/components/wrapper/UserMenu.tsx +0 -384
- package/src/components/wrapper/index.tsx +0 -8
- package/src/helpers/AnalyticsHelper.ts +0 -44
- package/src/helpers/AppearanceHelper.ts +0 -73
- package/src/helpers/ArrayHelper.ts +0 -87
- package/src/helpers/CurrencyHelper.ts +0 -10
- package/src/helpers/DateHelper.ts +0 -104
- package/src/helpers/ErrorHelper.ts +0 -41
- package/src/helpers/EventHelper.ts +0 -49
- package/src/helpers/FileHelper.ts +0 -31
- package/src/helpers/Locale.ts +0 -457
- package/src/helpers/NotificationService.ts +0 -233
- package/src/helpers/PersonHelper.ts +0 -62
- package/src/helpers/SlugHelper.ts +0 -27
- package/src/helpers/SocketHelper.ts +0 -183
- package/src/helpers/UniqueIdHelper.ts +0 -36
- package/src/helpers/UserHelper.ts +0 -103
- package/src/helpers/createEmotionCache.ts +0 -17
- package/src/helpers/index.ts +0 -58
- package/src/hooks/index.ts +0 -3
- package/src/hooks/useMountedState.ts +0 -18
- package/src/hooks/useNotifications.ts +0 -95
- package/src/index.ts +0 -3
- package/src/types/interface-extensions.d.ts +0 -12
- 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,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';
|