@asd20/ui-next 2.35.2 → 2.35.4
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,19 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [2.35.4](https://github.com/academydistrict20/asd20-ui-next/compare/ui-next-v2.35.3...ui-next-v2.35.4) (2026-11-09)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
* verbiage fixes to records request form ([ccee7d0](https://github.com/academydistrict20/asd20-ui-next/commit/ccee7d0071d450f3ef0a458d1f798faef3740cc2))
|
|
9
|
+
|
|
10
|
+
## [2.35.3](https://github.com/academydistrict20/asd20-ui-next/compare/ui-next-v2.35.2...ui-next-v2.35.3) (2026-11-09)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Bug Fixes
|
|
14
|
+
|
|
15
|
+
* give transcript requests a prominent alternate path in the student records form ([1250f67](https://github.com/academydistrict20/asd20-ui-next/commit/1250f6773bfa0047a234ea064463408cf4e51713))
|
|
16
|
+
|
|
3
17
|
## [2.35.2](https://github.com/academydistrict20/asd20-ui-next/compare/ui-next-v2.35.1...ui-next-v2.35.2) (2026-11-04)
|
|
4
18
|
|
|
5
19
|
|
package/package.json
CHANGED
|
@@ -243,6 +243,7 @@ export default {
|
|
|
243
243
|
components: { Asd20Button, Asd20Checkbox, Asd20TextAreaInput, Asd20TextInput },
|
|
244
244
|
props: {
|
|
245
245
|
field: { type: Object, required: true },
|
|
246
|
+
showRequiredLabel: { type: Boolean, default: true },
|
|
246
247
|
modelValue: { type: [String, Boolean, Array], default: '' },
|
|
247
248
|
errors: { type: Array, default: () => [] },
|
|
248
249
|
inputId: { type: String, required: true },
|
|
@@ -265,7 +266,7 @@ export default {
|
|
|
265
266
|
: undefined
|
|
266
267
|
},
|
|
267
268
|
displayLabel() {
|
|
268
|
-
return this.field.required
|
|
269
|
+
return this.field.required && this.showRequiredLabel
|
|
269
270
|
? `${this.field.label} (required)`
|
|
270
271
|
: this.field.label
|
|
271
272
|
},
|
|
@@ -49,8 +49,38 @@
|
|
|
49
49
|
novalidate
|
|
50
50
|
@submit.prevent="validateBeforeCaptcha"
|
|
51
51
|
>
|
|
52
|
+
<asd20-public-form-field
|
|
53
|
+
v-if="requestTypeField"
|
|
54
|
+
class="asd20-public-form-modal__requester-choice asd20-public-form-modal__records-choice"
|
|
55
|
+
:field="requestTypeField"
|
|
56
|
+
:show-required-label="false"
|
|
57
|
+
:input-id="fieldId('requesterType')"
|
|
58
|
+
:model-value="values.requesterType"
|
|
59
|
+
:errors="validationErrors.requesterType || []"
|
|
60
|
+
:show-pristine-validation-errors="false"
|
|
61
|
+
@update:modelValue="updateValue('requesterType', $event)"
|
|
62
|
+
/>
|
|
63
|
+
|
|
64
|
+
<section v-if="isStudentRecordsForm && values.requesterType === 'transcriptOnly'">
|
|
65
|
+
<h3>Request a Transcript</h3>
|
|
66
|
+
<p>
|
|
67
|
+
For transcripts, graduation verification or replacement diplomas,
|
|
68
|
+
please use our dedicated Transcripts and Diplomas page.
|
|
69
|
+
</p>
|
|
70
|
+
<p>
|
|
71
|
+
<a :href="transcriptsHref">Go to Transcripts and Diplomas</a>
|
|
72
|
+
</p>
|
|
73
|
+
</section>
|
|
74
|
+
|
|
75
|
+
<template v-if="isStudentRecordsForm && showRequestFields">
|
|
76
|
+
<h3>{{ recordsRequestTitle }}</h3>
|
|
77
|
+
<p v-if="requestTypeDescription">
|
|
78
|
+
{{ requestTypeDescription }}
|
|
79
|
+
</p>
|
|
80
|
+
</template>
|
|
81
|
+
|
|
52
82
|
<p
|
|
53
|
-
v-if="formDefinition.introduction"
|
|
83
|
+
v-if="showRequestFields && formDefinition.introduction"
|
|
54
84
|
class="asd20-public-form-modal__introduction"
|
|
55
85
|
>
|
|
56
86
|
<template v-if="inlineIntroductionParts">
|
|
@@ -65,7 +95,7 @@
|
|
|
65
95
|
</p>
|
|
66
96
|
<p
|
|
67
97
|
v-if="
|
|
68
|
-
formDefinition.introductionLink &&
|
|
98
|
+
showRequestFields && formDefinition.introductionLink &&
|
|
69
99
|
!formDefinition.introductionLink.inline
|
|
70
100
|
"
|
|
71
101
|
>
|
|
@@ -128,6 +158,7 @@
|
|
|
128
158
|
</div>
|
|
129
159
|
|
|
130
160
|
<div
|
|
161
|
+
v-if="showRequestFields"
|
|
131
162
|
class="asd20-public-form-modal__submit"
|
|
132
163
|
aria-live="polite"
|
|
133
164
|
>
|
|
@@ -197,6 +228,29 @@
|
|
|
197
228
|
</div>
|
|
198
229
|
</form>
|
|
199
230
|
</asd20-viewport>
|
|
231
|
+
|
|
232
|
+
<asd20-modal
|
|
233
|
+
:open="open && showTranscriptReminder"
|
|
234
|
+
title="Transcript-only request"
|
|
235
|
+
dismissable
|
|
236
|
+
@dismiss="showTranscriptReminder = false"
|
|
237
|
+
>
|
|
238
|
+
<asd20-viewport scrollable>
|
|
239
|
+
<p>
|
|
240
|
+
You selected only a transcript. Most transcript-only requests should be
|
|
241
|
+
submitted through our Transcripts and Diplomas page.
|
|
242
|
+
</p>
|
|
243
|
+
<p>
|
|
244
|
+
<a :href="transcriptsHref">Go to Transcripts and Diplomas</a>
|
|
245
|
+
</p>
|
|
246
|
+
<asd20-button
|
|
247
|
+
label="Back to request"
|
|
248
|
+
horizontal
|
|
249
|
+
bordered
|
|
250
|
+
@click="showTranscriptReminder = false"
|
|
251
|
+
/>
|
|
252
|
+
</asd20-viewport>
|
|
253
|
+
</asd20-modal>
|
|
200
254
|
</asd20-modal>
|
|
201
255
|
</template>
|
|
202
256
|
|
|
@@ -221,6 +275,11 @@ import {
|
|
|
221
275
|
submitPublicForm,
|
|
222
276
|
} from '../../../helpers/publicForms'
|
|
223
277
|
import createComponentInstanceId from '../../../utils/createComponentInstanceId'
|
|
278
|
+
import {
|
|
279
|
+
studentRecordsPresentation,
|
|
280
|
+
studentRecordsSubmissionValues,
|
|
281
|
+
usesStudentRecordsRouting,
|
|
282
|
+
} from '../../../helpers/studentRecordsRequest'
|
|
224
283
|
|
|
225
284
|
const DEFAULT_CAPTCHA_SITEKEY = '6LfidKoUAAAAAFqr3QEbia3jIkecsZyxBYlMvWrX'
|
|
226
285
|
|
|
@@ -253,6 +312,9 @@ export default {
|
|
|
253
312
|
instanceId,
|
|
254
313
|
honeypotId: `${instanceId}-website`,
|
|
255
314
|
formDefinition: null,
|
|
315
|
+
serviceDefinition: null,
|
|
316
|
+
transcriptsHref: 'https://www.asd20.org/transcripts-and-diplomas/',
|
|
317
|
+
showTranscriptReminder: false,
|
|
256
318
|
values: {},
|
|
257
319
|
honeypot: '',
|
|
258
320
|
validationErrors: {},
|
|
@@ -271,12 +333,15 @@ export default {
|
|
|
271
333
|
canSubmit() {
|
|
272
334
|
return Boolean(
|
|
273
335
|
this.formDefinition &&
|
|
336
|
+
this.showRequestFields &&
|
|
274
337
|
!this.sending &&
|
|
275
338
|
validatePublicFormValues(this.formDefinition, this.values).valid
|
|
276
339
|
)
|
|
277
340
|
},
|
|
278
341
|
visibleFields() {
|
|
279
342
|
return (this.formDefinition?.fields || [])
|
|
343
|
+
.filter(field => !this.isStudentRecordsForm ||
|
|
344
|
+
(this.showRequestFields && field.name !== 'requesterType'))
|
|
280
345
|
.filter(field => isPublicFormFieldVisible(field, this.values))
|
|
281
346
|
.map(field => ({
|
|
282
347
|
...field,
|
|
@@ -286,6 +351,27 @@ export default {
|
|
|
286
351
|
modalTitle() {
|
|
287
352
|
return this.formDefinition?.title || 'Public Form'
|
|
288
353
|
},
|
|
354
|
+
isStudentRecordsForm() {
|
|
355
|
+
return usesStudentRecordsRouting(this.formDefinition)
|
|
356
|
+
},
|
|
357
|
+
requestTypeField() {
|
|
358
|
+
return this.isStudentRecordsForm
|
|
359
|
+
? this.formDefinition.fields.find(field => field.name === 'requesterType')
|
|
360
|
+
: null
|
|
361
|
+
},
|
|
362
|
+
showRequestFields() {
|
|
363
|
+
return !this.isStudentRecordsForm ||
|
|
364
|
+
['schoolTransfer', 'studentRecords'].includes(this.values.requesterType)
|
|
365
|
+
},
|
|
366
|
+
recordsRequestTitle() {
|
|
367
|
+
return this.values.requesterType === 'schoolTransfer'
|
|
368
|
+
? 'School Transfer Records Request'
|
|
369
|
+
: 'Student Records Request (FERPA Requests Only)'
|
|
370
|
+
},
|
|
371
|
+
requestTypeDescription() {
|
|
372
|
+
return this.serviceDefinition?.fields.find(field => field.name === 'requesterType')
|
|
373
|
+
?.options.find(option => option.value === this.values.requesterType)?.description
|
|
374
|
+
},
|
|
289
375
|
inlineIntroductionParts() {
|
|
290
376
|
const introduction = this.formDefinition?.introduction || ''
|
|
291
377
|
const link = this.formDefinition?.introductionLink
|
|
@@ -371,6 +457,7 @@ export default {
|
|
|
371
457
|
this.validationErrors = {}
|
|
372
458
|
this.validationAttempted = false
|
|
373
459
|
this.honeypot = ''
|
|
460
|
+
this.showTranscriptReminder = false
|
|
374
461
|
|
|
375
462
|
try {
|
|
376
463
|
const expectedId = normalizeFormId(this.formId)
|
|
@@ -387,11 +474,13 @@ export default {
|
|
|
387
474
|
)
|
|
388
475
|
if (sequence !== this.loadSequence) return
|
|
389
476
|
|
|
390
|
-
this.
|
|
391
|
-
this.
|
|
477
|
+
this.serviceDefinition = normalized
|
|
478
|
+
this.formDefinition = studentRecordsPresentation(normalized)
|
|
479
|
+
this.values = createPublicFormValues(this.formDefinition)
|
|
392
480
|
} catch (_error) {
|
|
393
481
|
if (sequence !== this.loadSequence) return
|
|
394
482
|
this.formDefinition = null
|
|
483
|
+
this.serviceDefinition = null
|
|
395
484
|
this.definitionError =
|
|
396
485
|
'This form could not be loaded. Please try again later or use another contact option on this page.'
|
|
397
486
|
this.$nextTick(() => this.$refs.definitionError?.focus?.())
|
|
@@ -406,6 +495,15 @@ export default {
|
|
|
406
495
|
return field.showCharacterCount === true
|
|
407
496
|
},
|
|
408
497
|
updateValue(name, value) {
|
|
498
|
+
if (this.isStudentRecordsForm && name === 'requesterType' && this.values[name] !== value) {
|
|
499
|
+
this.values.transferRecordTypes = []
|
|
500
|
+
this.values.otherRecordsDescription = ''
|
|
501
|
+
this.showTranscriptReminder = false
|
|
502
|
+
this.validationErrors = {}
|
|
503
|
+
this.validationAttempted = false
|
|
504
|
+
this.feedbackMessage = ''
|
|
505
|
+
this.submissionFailed = false
|
|
506
|
+
}
|
|
409
507
|
this.values[name] = value
|
|
410
508
|
for (const field of this.formDefinition.fields) {
|
|
411
509
|
if (!isPublicFormFieldVisible(field, this.values)) {
|
|
@@ -416,7 +514,7 @@ export default {
|
|
|
416
514
|
if (this.validationAttempted) this.validate(false)
|
|
417
515
|
},
|
|
418
516
|
validate(focusSummary = true) {
|
|
419
|
-
if (!this.formDefinition) return false
|
|
517
|
+
if (!this.formDefinition || !this.showRequestFields) return false
|
|
420
518
|
const result = validatePublicFormValues(this.formDefinition, this.values)
|
|
421
519
|
this.values = result.values
|
|
422
520
|
this.validationErrors = result.errors
|
|
@@ -428,12 +526,21 @@ export default {
|
|
|
428
526
|
return result.valid
|
|
429
527
|
},
|
|
430
528
|
validateBeforeCaptcha(event) {
|
|
431
|
-
if (this.sending || !this.validate()) {
|
|
529
|
+
if (this.sending || !this.validate() || this.checkTranscriptOnly()) {
|
|
432
530
|
event?.preventDefault?.()
|
|
433
531
|
return false
|
|
434
532
|
}
|
|
435
533
|
return true
|
|
436
534
|
},
|
|
535
|
+
checkTranscriptOnly() {
|
|
536
|
+
if (this.isStudentRecordsForm &&
|
|
537
|
+
this.values.transferRecordTypes.length === 1 &&
|
|
538
|
+
this.values.transferRecordTypes[0] === 'academicRecords') {
|
|
539
|
+
this.showTranscriptReminder = true
|
|
540
|
+
return true
|
|
541
|
+
}
|
|
542
|
+
return false
|
|
543
|
+
},
|
|
437
544
|
focusField(name) {
|
|
438
545
|
document.getElementById(this.fieldId(name))?.focus?.()
|
|
439
546
|
},
|
|
@@ -480,7 +587,7 @@ export default {
|
|
|
480
587
|
this.$nextTick(() => this.$refs.feedback?.focus?.())
|
|
481
588
|
},
|
|
482
589
|
async submitForm(captchaToken = '') {
|
|
483
|
-
if (this.sending || !this.validate()) return
|
|
590
|
+
if (this.sending || !this.validate() || this.checkTranscriptOnly()) return
|
|
484
591
|
if (this.captchaEnabled && !captchaToken) {
|
|
485
592
|
this.showFeedback(
|
|
486
593
|
'Verification is required before submitting this form.'
|
|
@@ -494,8 +601,11 @@ export default {
|
|
|
494
601
|
try {
|
|
495
602
|
const payload = {
|
|
496
603
|
...(await serializePublicFormValues(
|
|
497
|
-
this.
|
|
498
|
-
|
|
604
|
+
this.serviceDefinition,
|
|
605
|
+
validatePublicFormValues(
|
|
606
|
+
this.serviceDefinition,
|
|
607
|
+
studentRecordsSubmissionValues(this.serviceDefinition, this.values)
|
|
608
|
+
).values
|
|
499
609
|
)),
|
|
500
610
|
website: this.honeypot,
|
|
501
611
|
captchaToken,
|
|
@@ -555,6 +665,12 @@ export default {
|
|
|
555
665
|
align-items: flex-start;
|
|
556
666
|
}
|
|
557
667
|
|
|
668
|
+
&__records-choice :deep(legend) {
|
|
669
|
+
font-size: 1.5rem;
|
|
670
|
+
font-weight: 700;
|
|
671
|
+
margin-bottom: 0.5rem;
|
|
672
|
+
}
|
|
673
|
+
|
|
558
674
|
&__state,
|
|
559
675
|
&__submit {
|
|
560
676
|
align-items: center;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
// Presentation for the unified form; the service still accepts its v8 wire fields.
|
|
2
|
+
export function usesStudentRecordsRouting(definition) {
|
|
3
|
+
return (
|
|
4
|
+
definition?.id === 'student-records-request' &&
|
|
5
|
+
definition.fields.some(field =>
|
|
6
|
+
field.name === 'requesterType' &&
|
|
7
|
+
field.options?.some(option => option.value === 'schoolTransfer')
|
|
8
|
+
) &&
|
|
9
|
+
definition.fields.some(field => field.name === 'transferRecordTypes')
|
|
10
|
+
)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function studentRecordsPresentation(definition) {
|
|
14
|
+
if (!usesStudentRecordsRouting(definition)) return definition
|
|
15
|
+
|
|
16
|
+
return {
|
|
17
|
+
...definition,
|
|
18
|
+
fields: definition.fields
|
|
19
|
+
.filter(field => field.name !== 'recordsRequested')
|
|
20
|
+
.map(field => {
|
|
21
|
+
if (field.name === 'requesterType') {
|
|
22
|
+
return {
|
|
23
|
+
...field,
|
|
24
|
+
label: 'What records do you need?',
|
|
25
|
+
options: [
|
|
26
|
+
{
|
|
27
|
+
label: 'Transcript or graduation verification only',
|
|
28
|
+
value: 'transcriptOnly',
|
|
29
|
+
},
|
|
30
|
+
{ label: 'School Transfer Records Request', value: 'schoolTransfer' },
|
|
31
|
+
{ label: 'Student Records Request (FERPA only)', value: 'studentRecords' },
|
|
32
|
+
],
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
if (field.name === 'transferRecordTypes') {
|
|
36
|
+
return {
|
|
37
|
+
...field,
|
|
38
|
+
visibleWhen: {
|
|
39
|
+
fieldName: 'requesterType',
|
|
40
|
+
values: ['schoolTransfer', 'studentRecords'],
|
|
41
|
+
},
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return field
|
|
45
|
+
}),
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function studentRecordsSubmissionValues(definition, values) {
|
|
50
|
+
if (
|
|
51
|
+
!usesStudentRecordsRouting(definition) ||
|
|
52
|
+
values.requesterType !== 'studentRecords'
|
|
53
|
+
) {
|
|
54
|
+
return values
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const options = definition.fields.find(
|
|
58
|
+
field => field.name === 'transferRecordTypes'
|
|
59
|
+
).options
|
|
60
|
+
const labels = values.transferRecordTypes.map(value => {
|
|
61
|
+
const label = options.find(option => option.value === value).label
|
|
62
|
+
return value === 'otherRecords'
|
|
63
|
+
? `${label}: ${values.otherRecordsDescription}`
|
|
64
|
+
: label
|
|
65
|
+
})
|
|
66
|
+
return {
|
|
67
|
+
...values,
|
|
68
|
+
recordsRequested: labels.join('\n'),
|
|
69
|
+
transferRecordTypes: [],
|
|
70
|
+
otherRecordsDescription: '',
|
|
71
|
+
}
|
|
72
|
+
}
|