@churchapps/apphelper-login 0.6.14 → 0.6.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/dist/LoginPage.d.ts +1 -0
  2. package/dist/LoginPage.d.ts.map +1 -1
  3. package/dist/LoginPage.js +14 -3
  4. package/dist/LoginPage.js.map +1 -1
  5. package/dist/LogoutPage.d.ts.map +1 -1
  6. package/dist/LogoutPage.js +1 -1
  7. package/dist/LogoutPage.js.map +1 -1
  8. package/dist/components/Forgot.d.ts.map +1 -1
  9. package/dist/components/Forgot.js +71 -83
  10. package/dist/components/Forgot.js.map +1 -1
  11. package/dist/components/Login.d.ts.map +1 -1
  12. package/dist/components/Login.js +86 -106
  13. package/dist/components/Login.js.map +1 -1
  14. package/dist/components/LoginSetPassword.d.ts.map +1 -1
  15. package/dist/components/LoginSetPassword.js +70 -94
  16. package/dist/components/LoginSetPassword.js.map +1 -1
  17. package/dist/components/Register.d.ts.map +1 -1
  18. package/dist/components/Register.js +104 -132
  19. package/dist/components/Register.js.map +1 -1
  20. package/dist/components/SelectChurchModal.d.ts.map +1 -1
  21. package/dist/components/SelectChurchModal.js +2 -1
  22. package/dist/components/SelectChurchModal.js.map +1 -1
  23. package/dist/components/SelectChurchRegister.d.ts.map +1 -1
  24. package/dist/components/SelectChurchRegister.js +11 -5
  25. package/dist/components/SelectChurchRegister.js.map +1 -1
  26. package/dist/components/SelectChurchSearch.d.ts.map +1 -1
  27. package/dist/components/SelectChurchSearch.js +3 -2
  28. package/dist/components/SelectChurchSearch.js.map +1 -1
  29. package/dist/components/SelectableChurch.js +1 -1
  30. package/dist/components/SelectableChurch.js.map +1 -1
  31. package/dist/helpers/AnalyticsHelper.d.ts.map +1 -1
  32. package/dist/helpers/AnalyticsHelper.js +3 -3
  33. package/dist/helpers/AnalyticsHelper.js.map +1 -1
  34. package/dist/helpers/Locale.d.ts.map +1 -1
  35. package/dist/helpers/Locale.js +9 -11
  36. package/dist/helpers/Locale.js.map +1 -1
  37. package/package.json +98 -57
  38. package/src/LoginPage.tsx +0 -314
  39. package/src/LogoutPage.tsx +0 -43
  40. package/src/components/Forgot.tsx +0 -247
  41. package/src/components/Login.tsx +0 -304
  42. package/src/components/LoginSetPassword.tsx +0 -296
  43. package/src/components/Register.tsx +0 -371
  44. package/src/components/SelectChurchModal.tsx +0 -88
  45. package/src/components/SelectChurchRegister.tsx +0 -88
  46. package/src/components/SelectChurchSearch.tsx +0 -85
  47. package/src/components/SelectableChurch.tsx +0 -114
  48. package/src/helpers/AnalyticsHelper.ts +0 -44
  49. package/src/helpers/Locale.ts +0 -248
  50. package/src/helpers/index.ts +0 -2
  51. package/src/index.ts +0 -11
  52. package/tsconfig.json +0 -30
@@ -1,247 +0,0 @@
1
- "use client";
2
-
3
- import React, { FormEventHandler } from "react";
4
- import { ApiHelper } from "@churchapps/helpers";
5
- import { Locale } from "../helpers";
6
- import { ResetPasswordRequestInterface, ResetPasswordResponseInterface } from "@churchapps/helpers";
7
- import { TextField, Typography, Card, CardContent, Button } from "@mui/material";
8
-
9
- interface Props {
10
- registerCallback: () => void,
11
- loginCallback: () => void
12
- }
13
-
14
- export const Forgot: React.FC<Props> = props => {
15
- const [errors, setErrors] = React.useState([]);
16
- const [successMessage, setSuccessMessage] = React.useState<React.ReactElement>(null);
17
- const [email, setEmail] = React.useState("");
18
- const [isSubmitting, setIsSubmitting] = React.useState(false);
19
-
20
- const handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement>) => {
21
- setEmail(e.target.value);
22
- }
23
-
24
- const validateEmail = (email: string) => (/^\w+([.-]?\w+)*@\w+([.-]?\w+)*(\.\w{2,3})+$/.test(email))
25
-
26
- const validate = () => {
27
- const result = [];
28
- if (!email) result.push(Locale.label("login.validate.email"));
29
- else if (!validateEmail(email)) result.push(Locale.label("login.validate.email"));
30
- setErrors(result);
31
- return result.length === 0;
32
- }
33
-
34
- const reset: FormEventHandler = (e) => {
35
- e.preventDefault();
36
- if (validate()) {
37
- setIsSubmitting(true);
38
- let req: ResetPasswordRequestInterface = { userEmail: email };
39
- ApiHelper.postAnonymous("/users/forgot", req, "MembershipApi").then((resp: ResetPasswordResponseInterface) => {
40
- if (resp.emailed) {
41
- setErrors([]);
42
- setSuccessMessage(
43
- <Typography textAlign="center" marginTop="35px">
44
- {Locale.label("login.resetSent")} <br /><br />
45
- <button
46
- style={{
47
- background: 'none',
48
- border: 'none',
49
- color: '#3b82f6',
50
- fontSize: '14px',
51
- cursor: 'pointer',
52
- textDecoration: 'none'
53
- }}
54
- onMouseOver={(e) => e.currentTarget.style.textDecoration = 'underline'}
55
- onMouseOut={(e) => e.currentTarget.style.textDecoration = 'none'}
56
- onClick={(e) => { e.preventDefault(); props.loginCallback(); }}
57
- >
58
- {Locale.label("login.goLogin")}
59
- </button>
60
- </Typography>
61
- );
62
- setEmail("");
63
- } else {
64
- setErrors(["We could not find an account with this email address"]);
65
- setSuccessMessage(<></>);
66
- }
67
- }).finally(() => { setIsSubmitting(false); });
68
- }
69
- }
70
-
71
- return (
72
- <div style={{ minHeight: '100vh', backgroundColor: 'white', display: 'flex', alignItems: 'center', justifyContent: 'center', padding: '16px' }}>
73
- <Card sx={{
74
- width: '100%',
75
- maxWidth: { xs: '400px', sm: '500px' },
76
- backgroundColor: 'white',
77
- border: '1px solid #e5e7eb',
78
- boxShadow: '0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05)'
79
- }}>
80
- <CardContent sx={{ textAlign: 'center', padding: '32px' }}>
81
- <div style={{ marginBottom: '32px' }}>
82
- <img
83
- src="/images/logo-login.png"
84
- alt="Church Logo"
85
- style={{
86
- maxWidth: '100%',
87
- width: 'auto',
88
- height: 'auto',
89
- maxHeight: '80px',
90
- marginBottom: '16px',
91
- objectFit: 'contain'
92
- }}
93
- />
94
- </div>
95
- <Typography
96
- component="h1"
97
- sx={{
98
- fontSize: '24px',
99
- fontWeight: 'bold',
100
- color: '#111827',
101
- marginBottom: '8px'
102
- }}
103
- >
104
- {Locale.label("login.resetPassword")}
105
- </Typography>
106
- <Typography
107
- sx={{
108
- color: '#6b7280',
109
- marginBottom: '32px'
110
- }}
111
- >
112
- Enter your email to receive password reset instructions
113
- </Typography>
114
-
115
- <form onSubmit={reset} style={{ display: 'flex', flexDirection: 'column', gap: '16px' }}>
116
- {errors.length > 0 && (
117
- <div style={{
118
- backgroundColor: '#fef2f2',
119
- border: '1px solid #fecaca',
120
- borderRadius: '6px',
121
- padding: '12px',
122
- textAlign: 'left'
123
- }}>
124
- {errors.map((error) => (
125
- <div key={error} style={{ color: '#dc2626', fontSize: '14px' }}>{error}</div>
126
- ))}
127
- </div>
128
- )}
129
-
130
- {successMessage ? (
131
- <div style={{ textAlign: 'center', display: 'flex', flexDirection: 'column', gap: '16px' }}>
132
- {successMessage}
133
- </div>
134
- ) : (
135
- <>
136
- <Typography variant="body2" sx={{ color: '#6b7280', fontSize: '14px', textAlign: 'left' }}>
137
- {Locale.label("login.resetInstructions")}
138
- </Typography>
139
-
140
- <div style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}>
141
- <label htmlFor="forgot-email" style={{ fontSize: '14px', fontWeight: 500, color: '#374151', textAlign: 'left' }}>
142
- {Locale.label("login.email")}
143
- </label>
144
- <TextField
145
- id="forgot-email"
146
- name="forgot-email"
147
- type="email"
148
- placeholder={Locale.label("login.email")}
149
- value={email}
150
- onChange={handleChange}
151
- autoFocus
152
- required
153
- autoComplete="email"
154
- variant="outlined"
155
- fullWidth
156
- onKeyDown={(e: React.KeyboardEvent<HTMLInputElement>) => e.key === "Enter" && reset}
157
- sx={{
158
- '& .MuiOutlinedInput-root': {
159
- backgroundColor: 'white',
160
- '& fieldset': {
161
- borderColor: '#d1d5db'
162
- },
163
- '&:hover fieldset': {
164
- borderColor: '#d1d5db'
165
- },
166
- '&.Mui-focused fieldset': {
167
- borderColor: '#3b82f6'
168
- },
169
- '& input': {
170
- color: '#111827',
171
- fontSize: '16px'
172
- }
173
- },
174
- '& .MuiInputLabel-root': {
175
- display: 'none'
176
- }
177
- }}
178
- />
179
- </div>
180
-
181
- <Button
182
- type="submit"
183
- variant="contained"
184
- fullWidth
185
- disabled={isSubmitting}
186
- sx={{
187
- backgroundColor: 'hsl(218, 85%, 55%)',
188
- color: 'white',
189
- padding: '12px',
190
- textTransform: 'none',
191
- fontSize: '16px',
192
- fontWeight: 500,
193
- borderRadius: '6px',
194
- '&:hover': {
195
- backgroundColor: 'hsl(218, 85%, 50%)'
196
- },
197
- '&:disabled': {
198
- backgroundColor: '#9ca3af'
199
- }
200
- }}
201
- >
202
- {isSubmitting ? "Sending..." : Locale.label("login.reset")}
203
- </Button>
204
-
205
- <div style={{ textAlign: 'center', display: 'flex', justifyContent: 'center', alignItems: 'center', gap: '8px' }}>
206
- <button
207
- type="button"
208
- onClick={(e) => { e.preventDefault(); props.registerCallback(); }}
209
- style={{
210
- background: 'none',
211
- border: 'none',
212
- color: '#3b82f6',
213
- fontSize: '14px',
214
- cursor: 'pointer',
215
- textDecoration: 'none'
216
- }}
217
- onMouseOver={(e) => e.currentTarget.style.textDecoration = 'underline'}
218
- onMouseOut={(e) => e.currentTarget.style.textDecoration = 'none'}
219
- >
220
- {Locale.label("login.register")}
221
- </button>
222
- <span style={{ fontSize: '14px', color: '#6b7280' }}>|</span>
223
- <button
224
- type="button"
225
- onClick={(e) => { e.preventDefault(); props.loginCallback(); }}
226
- style={{
227
- background: 'none',
228
- border: 'none',
229
- color: '#3b82f6',
230
- fontSize: '14px',
231
- cursor: 'pointer',
232
- textDecoration: 'none'
233
- }}
234
- onMouseOver={(e) => e.currentTarget.style.textDecoration = 'underline'}
235
- onMouseOut={(e) => e.currentTarget.style.textDecoration = 'none'}
236
- >
237
- {Locale.label("login.login")}
238
- </button>
239
- </div>
240
- </>
241
- )}
242
- </form>
243
- </CardContent>
244
- </Card>
245
- </div>
246
- );
247
- }
@@ -1,304 +0,0 @@
1
- "use client";
2
-
3
- import React from "react";
4
- import { TextField, PaperProps, InputAdornment, IconButton, Card, CardContent, Typography, Button } from "@mui/material";
5
- import { Visibility, VisibilityOff } from "@mui/icons-material";
6
- import { Locale } from "../helpers";
7
-
8
- interface Props {
9
- login: (data: any) => void,
10
- isSubmitting: boolean,
11
- setShowRegister: (showRegister: boolean) => void,
12
- setShowForgot: (showForgot: boolean) => void,
13
- setErrors: (errors: string[]) => void;
14
- mainContainerCssProps?: PaperProps;
15
- defaultEmail?: string;
16
- defaultPassword?: string;
17
- showFooter?: boolean;
18
- }
19
-
20
- export const Login: React.FC<Props> = ({ mainContainerCssProps = {}, ...props }) => {
21
- const [email, setEmail] = React.useState(props.defaultEmail || "");
22
- const [password, setPassword] = React.useState(props.defaultPassword || "");
23
- const [showPassword, setShowPassword] = React.useState(false);
24
-
25
- const validateEmail = (email: string) => (/^\w+([.-]?\w+)*@\w+([.-]?\w+)*(.\w{2,3})+$/.test(email))
26
-
27
- const validate = () => {
28
- const result = [];
29
- if (!email) result.push(Locale.label("login.validate.email"));
30
- else if (!validateEmail(email)) result.push(Locale.label("login.validate.email"));
31
- if (!password) result.push(Locale.label("login.validate.password"));
32
- props.setErrors(result);
33
- return result.length === 0;
34
- }
35
-
36
- const submitLogin = () => {
37
- if (validate()) props.login({ email, password });
38
- }
39
-
40
- const handleShowRegister = (e: React.MouseEvent) => {
41
- e.preventDefault();
42
- props.setShowRegister(true);
43
- }
44
-
45
- return (
46
- <div id="login-container" style={{ minHeight: '100vh', backgroundColor: 'white', display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', padding: '16px', position: 'relative' }}>
47
- <Card id="login-card" sx={{
48
- width: '100%',
49
- maxWidth: { xs: '400px', sm: '500px' },
50
- backgroundColor: 'white',
51
- border: '1px solid #e5e7eb',
52
- boxShadow: '0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05)',
53
- ...mainContainerCssProps
54
- }}>
55
- <CardContent id="login-content" sx={{ textAlign: 'center', padding: '32px' }}>
56
- <div id="login-logo" style={{ marginBottom: '32px' }}>
57
- <img
58
- id="login-logo-image"
59
- src="/images/logo-login.png"
60
- alt="Church Logo"
61
- style={{
62
- maxWidth: '100%',
63
- width: 'auto',
64
- height: 'auto',
65
- maxHeight: '80px',
66
- marginBottom: '16px',
67
- objectFit: 'contain'
68
- }}
69
- />
70
- </div>
71
- <Typography
72
- id="login-title"
73
- component="h1"
74
- sx={{
75
- fontSize: '24px',
76
- fontWeight: 'bold',
77
- color: '#111827',
78
- marginBottom: '8px'
79
- }}
80
- >
81
- {Locale.label("login.signInTitle")}
82
- </Typography>
83
- <Typography
84
- sx={{
85
- color: '#6b7280',
86
- marginBottom: '32px'
87
- }}
88
- >
89
- Enter your email and password to access your church
90
- </Typography>
91
-
92
- <form id="login-form" onSubmit={(e) => { e.preventDefault(); submitLogin(); }} style={{ display: 'flex', flexDirection: 'column', gap: '16px' }}>
93
- <div style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}>
94
- <label htmlFor="email" style={{ fontSize: '14px', fontWeight: 500, color: '#374151', textAlign: 'left' }}>
95
- {Locale.label("login.email")}
96
- </label>
97
- <TextField
98
- id="login-email-field"
99
- name="email"
100
- type="email"
101
- placeholder={Locale.label("login.email")}
102
- value={email}
103
- onChange={(e) => setEmail(e.target.value)}
104
- autoFocus
105
- required
106
- autoComplete="email"
107
- variant="outlined"
108
- fullWidth
109
- sx={{
110
- '& .MuiOutlinedInput-root': {
111
- backgroundColor: 'white',
112
- '& fieldset': {
113
- borderColor: '#d1d5db'
114
- },
115
- '&:hover fieldset': {
116
- borderColor: '#d1d5db'
117
- },
118
- '&.Mui-focused fieldset': {
119
- borderColor: '#3b82f6'
120
- },
121
- '& input': {
122
- color: '#111827',
123
- fontSize: '16px'
124
- }
125
- },
126
- '& .MuiInputLabel-root': {
127
- display: 'none'
128
- }
129
- }}
130
- />
131
- </div>
132
-
133
- <div style={{ display: 'flex', flexDirection: 'column', gap: '8px', position: 'relative' }}>
134
- <label htmlFor="password" style={{ fontSize: '14px', fontWeight: 500, color: '#374151', textAlign: 'left' }}>
135
- {Locale.label("login.password")}
136
- </label>
137
- <TextField
138
- id="login-password-field"
139
- name="password"
140
- type={showPassword ? "text" : "password"}
141
- placeholder={Locale.label("login.password")}
142
- value={password}
143
- onChange={(e) => setPassword(e.target.value)}
144
- required
145
- autoComplete="current-password"
146
- variant="outlined"
147
- fullWidth
148
- InputProps={{
149
- endAdornment: (
150
- <InputAdornment position="end">
151
- <IconButton
152
- id="password-visibility-toggle"
153
- aria-label="toggle password visibility"
154
- onClick={() => setShowPassword(!showPassword)}
155
- edge="end"
156
- sx={{ color: '#6b7280' }}
157
- >
158
- {showPassword ? <VisibilityOff /> : <Visibility />}
159
- </IconButton>
160
- </InputAdornment>
161
- )
162
- }}
163
- sx={{
164
- '& .MuiOutlinedInput-root': {
165
- backgroundColor: 'white',
166
- paddingRight: '10px',
167
- '& fieldset': {
168
- borderColor: '#d1d5db'
169
- },
170
- '&:hover fieldset': {
171
- borderColor: '#d1d5db'
172
- },
173
- '&.Mui-focused fieldset': {
174
- borderColor: '#3b82f6'
175
- },
176
- '& input': {
177
- color: '#111827',
178
- fontSize: '16px'
179
- }
180
- },
181
- '& .MuiInputLabel-root': {
182
- display: 'none'
183
- }
184
- }}
185
- />
186
- </div>
187
-
188
- <Button
189
- id="login-submit-button"
190
- type="submit"
191
- variant="contained"
192
- fullWidth
193
- disabled={props.isSubmitting}
194
- sx={{
195
- backgroundColor: 'hsl(218, 85%, 55%)',
196
- color: 'white',
197
- padding: '12px',
198
- textTransform: 'none',
199
- fontSize: '16px',
200
- fontWeight: 500,
201
- borderRadius: '6px',
202
- '&:hover': {
203
- backgroundColor: 'hsl(218, 85%, 50%)'
204
- },
205
- '&:disabled': {
206
- backgroundColor: '#9ca3af'
207
- }
208
- }}
209
- >
210
- {props.isSubmitting ? Locale.label("common.pleaseWait") : Locale.label("login.signIn")}
211
- </Button>
212
-
213
- <div id="login-links" style={{ textAlign: 'center', display: 'flex', flexDirection: 'column', gap: '8px' }}>
214
- <button
215
- id="forgot-password-link"
216
- type="button"
217
- onClick={(e) => { e.preventDefault(); props.setShowForgot(true); }}
218
- style={{
219
- background: 'none',
220
- border: 'none',
221
- color: '#3b82f6',
222
- fontSize: '14px',
223
- cursor: 'pointer',
224
- textDecoration: 'none'
225
- }}
226
- onMouseOver={(e) => e.currentTarget.style.textDecoration = 'underline'}
227
- onMouseOut={(e) => e.currentTarget.style.textDecoration = 'none'}
228
- >
229
- {Locale.label("login.forgot")}
230
- </button>
231
- <div style={{ fontSize: '14px', color: '#6b7280' }}>
232
- {Locale.label("login.noAcc")}{' '}
233
- <button
234
- id="register-link"
235
- type="button"
236
- onClick={handleShowRegister}
237
- style={{
238
- background: 'none',
239
- border: 'none',
240
- color: '#3b82f6',
241
- fontSize: '14px',
242
- cursor: 'pointer',
243
- textDecoration: 'none'
244
- }}
245
- onMouseOver={(e) => e.currentTarget.style.textDecoration = 'underline'}
246
- onMouseOut={(e) => e.currentTarget.style.textDecoration = 'none'}
247
- >
248
- {Locale.label("login.register")}
249
- </button>
250
- </div>
251
- </div>
252
- </form>
253
- </CardContent>
254
- </Card>
255
-
256
- {props.showFooter && (
257
- <div id="login-footer" style={{
258
- position: 'fixed',
259
- bottom: 0,
260
- left: 0,
261
- right: 0,
262
- backgroundColor: '#f9fafb',
263
- borderTop: '1px solid #e5e7eb',
264
- padding: '16px',
265
- display: 'flex',
266
- justifyContent: 'center',
267
- alignItems: 'center',
268
- gap: '24px',
269
- fontSize: '14px',
270
- color: '#6b7280'
271
- }}>
272
- <span style={{ marginRight: '8px' }}>Ministry that's supported, not sold</span>
273
- <a
274
- href="https://churchapps.org/partner"
275
- target="_blank"
276
- rel="noopener noreferrer"
277
- style={{
278
- color: '#3b82f6',
279
- textDecoration: 'none',
280
- display: 'flex',
281
- alignItems: 'center',
282
- gap: '4px'
283
- }}>
284
- <span>💙</span> Donate
285
- </a>
286
- <a
287
- href="https://github.com/ChurchApps/ChurchAppsSupport/issues"
288
- target="_blank"
289
- rel="noopener noreferrer"
290
- style={{
291
- color: '#6b7280',
292
- textDecoration: 'none',
293
- display: 'flex',
294
- alignItems: 'center',
295
- gap: '4px'
296
- }}
297
- >
298
- <span>🐛</span> Report Bug
299
- </a>
300
- </div>
301
- )}
302
- </div>
303
- );
304
- }