@asd20/ui-next 2.5.0 → 2.6.0
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/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
# [2.6.0](https://github.com/academydistrict20/asd20-ui-next/compare/ui-next-v2.5.0...ui-next-v2.6.0) (2026-05-04)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* add rate limit to emails and associated messaging ([66e4099](https://github.com/academydistrict20/asd20-ui-next/commit/66e40994ac924a9dad184227beae4dded0dbf2d9))
|
|
9
|
+
|
|
3
10
|
# [2.5.0](https://github.com/academydistrict20/asd20-ui-next/compare/ui-next-v2.4.3...ui-next-v2.5.0) (2026-05-04)
|
|
4
11
|
|
|
5
12
|
|
package/package.json
CHANGED
|
@@ -32,24 +32,37 @@
|
|
|
32
32
|
required
|
|
33
33
|
@validated="validationErrors.messageBody = $event"
|
|
34
34
|
/>
|
|
35
|
-
<
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
size="invisible"
|
|
39
|
-
@verify="captchaVerified"
|
|
35
|
+
<div
|
|
36
|
+
class="asd20-compose-email-modal__submit"
|
|
37
|
+
aria-live="polite"
|
|
40
38
|
>
|
|
41
|
-
<
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
39
|
+
<Recaptcha
|
|
40
|
+
v-if="!sending && !sendSucceeded"
|
|
41
|
+
sitekey="6LfidKoUAAAAAFqr3QEbia3jIkecsZyxBYlMvWrX"
|
|
42
|
+
:load-recaptcha-script="true"
|
|
43
|
+
size="invisible"
|
|
44
|
+
@verify="captchaVerified"
|
|
45
|
+
>
|
|
46
|
+
<asd20-button
|
|
47
|
+
:disabled="!isValid"
|
|
48
|
+
label="Send"
|
|
49
|
+
horizontal
|
|
50
|
+
centered
|
|
51
|
+
bordered
|
|
52
|
+
/>
|
|
53
|
+
</Recaptcha>
|
|
54
|
+
<asd20-spinner
|
|
55
|
+
v-else-if="sending"
|
|
56
|
+
size="sm"
|
|
47
57
|
/>
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
58
|
+
<p
|
|
59
|
+
v-else
|
|
60
|
+
class="asd20-compose-email-modal__success"
|
|
61
|
+
role="status"
|
|
62
|
+
>
|
|
63
|
+
Your message was sent.
|
|
64
|
+
</p>
|
|
65
|
+
</div>
|
|
53
66
|
</Asd20Viewport>
|
|
54
67
|
</asd20-modal>
|
|
55
68
|
</template>
|
|
@@ -87,6 +100,7 @@ export default {
|
|
|
87
100
|
},
|
|
88
101
|
composing: false,
|
|
89
102
|
sending: false,
|
|
103
|
+
sendSucceeded: false,
|
|
90
104
|
emailMessage: {
|
|
91
105
|
senderName: '',
|
|
92
106
|
senderEmail: '',
|
|
@@ -102,6 +116,11 @@ export default {
|
|
|
102
116
|
)
|
|
103
117
|
},
|
|
104
118
|
},
|
|
119
|
+
watch: {
|
|
120
|
+
open(value) {
|
|
121
|
+
if (value) this.resetSendState()
|
|
122
|
+
},
|
|
123
|
+
},
|
|
105
124
|
methods: {
|
|
106
125
|
resolveRuntimeConfig() {
|
|
107
126
|
const runtimeConfig =
|
|
@@ -129,7 +148,8 @@ export default {
|
|
|
129
148
|
)
|
|
130
149
|
},
|
|
131
150
|
validateEmailAddress({ value, validationErrors }) {
|
|
132
|
-
const pattern =
|
|
151
|
+
const pattern =
|
|
152
|
+
/^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/
|
|
133
153
|
const regex = RegExp(pattern)
|
|
134
154
|
if (!regex.test(value))
|
|
135
155
|
validationErrors.push('A valid email address is required')
|
|
@@ -149,15 +169,36 @@ export default {
|
|
|
149
169
|
captchaVerified(response) {
|
|
150
170
|
if (response) this.sendEmail(response)
|
|
151
171
|
},
|
|
172
|
+
resetSendState() {
|
|
173
|
+
this.sending = false
|
|
174
|
+
this.sendSucceeded = false
|
|
175
|
+
},
|
|
176
|
+
waitForSuccessMessage() {
|
|
177
|
+
return new Promise(resolve => {
|
|
178
|
+
setTimeout(resolve, 1200)
|
|
179
|
+
})
|
|
180
|
+
},
|
|
181
|
+
getSendEmailErrorStatus(error) {
|
|
182
|
+
if (error?.response?.status) return Number(error.response.status)
|
|
183
|
+
if (error?.status) return Number(error.status)
|
|
184
|
+
|
|
185
|
+
const statusMatch = String(error?.message || '').match(/\b(\d{3})\b/)
|
|
186
|
+
return statusMatch ? Number(statusMatch[1]) : null
|
|
187
|
+
},
|
|
188
|
+
getSendEmailFailureMessage(error) {
|
|
189
|
+
if (this.getSendEmailErrorStatus(error) === 429) {
|
|
190
|
+
return 'We could not send your message right now. Please wait and try again later.'
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
return 'Something went wrong while sending your email. Please try again later.'
|
|
194
|
+
},
|
|
152
195
|
async sendEmail(captchaToken = '') {
|
|
153
196
|
if (!this.isValid) return
|
|
154
197
|
|
|
155
198
|
const endpoint = this.resolveSendEmailEndpoint()
|
|
156
199
|
const sendEmailClient =
|
|
157
200
|
this.$sendEmail ||
|
|
158
|
-
(endpoint
|
|
159
|
-
? async (message) => await sendEmail(message, endpoint)
|
|
160
|
-
: null)
|
|
201
|
+
(endpoint ? async message => await sendEmail(message, endpoint) : null)
|
|
161
202
|
|
|
162
203
|
if (!sendEmailClient) {
|
|
163
204
|
console.error('Send email endpoint is not configured.')
|
|
@@ -168,6 +209,7 @@ export default {
|
|
|
168
209
|
}
|
|
169
210
|
|
|
170
211
|
this.sending = true
|
|
212
|
+
this.sendSucceeded = false
|
|
171
213
|
try {
|
|
172
214
|
await sendEmailClient(
|
|
173
215
|
Object.assign({}, this.emailMessage, {
|
|
@@ -175,12 +217,14 @@ export default {
|
|
|
175
217
|
captchaToken,
|
|
176
218
|
})
|
|
177
219
|
)
|
|
220
|
+
this.sending = false
|
|
221
|
+
this.sendSucceeded = true
|
|
222
|
+
await this.waitForSuccessMessage()
|
|
178
223
|
this.$emit('dismiss')
|
|
179
224
|
} catch (error) {
|
|
225
|
+
this.sendSucceeded = false
|
|
180
226
|
console.error('Email send failed:', error?.message || error)
|
|
181
|
-
alert(
|
|
182
|
-
'Something went wrong while sending your email. Please try again later.'
|
|
183
|
-
)
|
|
227
|
+
alert(this.getSendEmailFailureMessage(error))
|
|
184
228
|
} finally {
|
|
185
229
|
this.sending = false
|
|
186
230
|
}
|
|
@@ -196,6 +240,19 @@ export default {
|
|
|
196
240
|
& :deep(.asd20-modal__content .asd20-viewport) {
|
|
197
241
|
padding: space(1);
|
|
198
242
|
}
|
|
243
|
+
|
|
244
|
+
&__submit {
|
|
245
|
+
align-items: center;
|
|
246
|
+
display: flex;
|
|
247
|
+
justify-content: center;
|
|
248
|
+
min-height: 3rem;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
&__success {
|
|
252
|
+
font-weight: 600;
|
|
253
|
+
margin: 0;
|
|
254
|
+
text-align: center;
|
|
255
|
+
}
|
|
199
256
|
}
|
|
200
257
|
|
|
201
258
|
@media (min-width: 1024px) {
|
package/src/helpers/sendEmail.js
CHANGED
|
@@ -9,7 +9,7 @@ function emailMessageIsValid(emailMessage) {
|
|
|
9
9
|
)
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
export default async function(emailMessage, endpoint) {
|
|
12
|
+
export default async function (emailMessage, endpoint) {
|
|
13
13
|
// Check to see if the parameters is valid
|
|
14
14
|
if (!emailMessageIsValid(emailMessage))
|
|
15
15
|
throw new Error(
|
|
@@ -20,13 +20,15 @@ export default async function(emailMessage, endpoint) {
|
|
|
20
20
|
await axios.post(endpoint, emailMessage)
|
|
21
21
|
} catch (error) {
|
|
22
22
|
if (error.response) {
|
|
23
|
-
|
|
23
|
+
const sendError = new Error(
|
|
24
24
|
`A ${
|
|
25
25
|
error.response.status
|
|
26
26
|
} error occurred while attempting to send email: ${JSON.stringify(
|
|
27
27
|
error.response.data
|
|
28
28
|
)}`
|
|
29
29
|
)
|
|
30
|
+
sendError.response = error.response
|
|
31
|
+
throw sendError
|
|
30
32
|
} else {
|
|
31
33
|
// Something happened in setting up the request and triggered an Error
|
|
32
34
|
throw new Error(
|