@reqquest/ui 1.16.0 → 1.18.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/app/env.template +5 -0
- package/app/runtime/apply-env.sh +10 -0
- package/app/runtime/nginx.conf +37 -0
- package/app/src/app.css +21 -0
- package/app/src/app.d.ts +13 -0
- package/app/src/app.html +12 -0
- package/app/src/internal/api.ts +1707 -0
- package/app/src/internal/appreq-utils.ts +33 -0
- package/app/src/internal/components/AnnouncementBanner.svelte +47 -0
- package/app/src/internal/components/AppRequestCard.svelte +173 -0
- package/app/src/internal/components/ApplicantOptOutModal.svelte +78 -0
- package/app/src/internal/components/ApplicantProgramList.svelte +276 -0
- package/app/src/internal/components/ApplicantProgramListTooltip.svelte +44 -0
- package/app/src/internal/components/ApplicantPromptPage.svelte +136 -0
- package/app/src/internal/components/ApplicantPromptSkeleton.svelte +12 -0
- package/app/src/internal/components/ApplicationDetailsView.svelte +369 -0
- package/app/src/internal/components/ButtonLoadingIcon.svelte +28 -0
- package/{dist → app/src/internal}/components/DelayedSkeleton.svelte +2 -2
- package/app/src/internal/components/FieldNestedMultiselect.svelte +270 -0
- package/app/src/internal/components/IntroPanel.svelte +41 -0
- package/app/src/internal/components/MissingDefinitionNotification.svelte +20 -0
- package/app/src/internal/components/PeriodPanel.svelte +125 -0
- package/app/src/internal/components/ProgramStatusCell.svelte +134 -0
- package/app/src/internal/components/RenderDisplayComponent.svelte +42 -0
- package/app/src/internal/components/ReviewerActions.svelte +171 -0
- package/app/src/internal/components/ReviewerQuestions.svelte +444 -0
- package/app/src/internal/components/StatusMessageList.svelte +54 -0
- package/app/src/internal/components/SystemDeterminationNotice.svelte +37 -0
- package/app/src/internal/components/TabLinks.svelte +265 -0
- package/app/src/internal/components/WarningIconYellow.svelte +22 -0
- package/app/src/internal/components/index.ts +19 -0
- package/app/src/internal/csv.ts +30 -0
- package/app/src/internal/index.ts +8 -0
- package/app/src/internal/prompt-save-queue.ts +91 -0
- package/app/src/internal/prompt-utils.ts +40 -0
- package/app/src/internal/status-utils.ts +503 -0
- package/app/src/internal/stores/IStateStore.ts +5 -0
- package/app/src/internal/util.ts +27 -0
- package/app/src/routes/+error.svelte +4 -0
- package/app/src/routes/+layout.svelte +134 -0
- package/app/src/routes/+layout.ts +18 -0
- package/app/src/routes/+page.ts +14 -0
- package/app/src/routes/announcement/+page.svelte +164 -0
- package/app/src/routes/announcement/+page.ts +16 -0
- package/app/src/routes/dashboards/applicant/+page.svelte +494 -0
- package/app/src/routes/dashboards/applicant/+page.ts +95 -0
- package/app/src/routes/dashboards/reviewer/+page.svelte +143 -0
- package/app/src/routes/dashboards/reviewer/+page.ts +43 -0
- package/app/src/routes/periods/+page.svelte +158 -0
- package/app/src/routes/periods/+page.ts +13 -0
- package/app/src/routes/periods/[id]/configure/+page.svelte +150 -0
- package/app/src/routes/periods/[id]/configure/+page.ts +10 -0
- package/app/src/routes/requests/+page.svelte +246 -0
- package/app/src/routes/requests/+page.ts +24 -0
- package/app/src/routes/requests/[id]/+layout.ts +11 -0
- package/app/src/routes/requests/[id]/accept/+layout.svelte +80 -0
- package/app/src/routes/requests/[id]/accept/+layout.ts +12 -0
- package/app/src/routes/requests/[id]/accept/+page.ts +13 -0
- package/app/src/routes/requests/[id]/accept/[promptId]/+page.svelte +7 -0
- package/app/src/routes/requests/[id]/accept/[promptId]/+page.ts +10 -0
- package/app/src/routes/requests/[id]/accept/review/+page.svelte +87 -0
- package/app/src/routes/requests/[id]/apply/+layout.svelte +181 -0
- package/app/src/routes/requests/[id]/apply/+layout.ts +12 -0
- package/app/src/routes/requests/[id]/apply/+page.ts +14 -0
- package/app/src/routes/requests/[id]/apply/ProgressNavContainer.svelte +42 -0
- package/app/src/routes/requests/[id]/apply/[promptId]/+page.svelte +23 -0
- package/app/src/routes/requests/[id]/apply/[promptId]/+page.ts +11 -0
- package/app/src/routes/requests/[id]/apply/programs/+page.svelte +71 -0
- package/app/src/routes/requests/[id]/apply/review/+page.svelte +102 -0
- package/app/src/routes/requests/[id]/approve/+layout.svelte +114 -0
- package/app/src/routes/requests/[id]/approve/+page.ts +14 -0
- package/app/src/routes/requests/[id]/approve/ApproveLayout.svelte +164 -0
- package/app/src/routes/requests/[id]/approve/[programKey]/+page.svelte +396 -0
- package/app/src/routes/requests/[id]/approve/[programKey]/+page.ts +15 -0
- package/app/src/routes/requests/[id]/approve/activity/+page.svelte +66 -0
- package/app/src/routes/requests/[id]/approve/activity/+page.ts +12 -0
- package/app/src/routes/requests/[id]/approve/export/+page.ts +12 -0
- package/app/src/routes/requests/[id]/approve/export/+page@[id].svelte +41 -0
- package/app/src/routes/requests/[id]/export/+page.svelte +32 -0
- package/app/src/routes/requests/[id]/export/+page.ts +12 -0
- package/app/src/routes/roles/+layout.svelte +16 -0
- package/app/src/routes/roles/+layout.ts +14 -0
- package/app/src/routes/roles/+page.svelte +225 -0
- package/app/src/routes/roles/+page.ts +13 -0
- package/app/src/routes/roles/[id]/+layout.ts +10 -0
- package/app/src/routes/roles/[id]/+page.svelte +335 -0
- package/app/src/routes/roles/[id]/+page.ts +10 -0
- package/app/src/routes/roles/[id]/ControlWithTooltip.svelte +15 -0
- package/app/src/routes/roles/[id]/TagsWithTooltip.svelte +42 -0
- package/app/src/routes/roles/users/+layout.ts +13 -0
- package/app/src/routes/roles/users/+page.svelte +46 -0
- package/app/src/routes/roles/users/+page.ts +90 -0
- package/app/static/favicon.png +0 -0
- package/bin/reqquest-ui.js +117 -0
- package/cli/config.js +105 -0
- package/cli/sync.js +296 -0
- package/cli/util.js +29 -0
- package/dist/components.md +0 -4
- package/dist/registry.js +1 -1
- package/dist/typed-client/schema.d.ts +25 -3
- package/dist/typed-client/schema.graphql +18 -2
- package/dist/typed-client/types.d.ts +9 -1
- package/dist/typed-client/types.js +29 -5
- package/package.json +29 -15
- package/dist/components/DelayedSkeleton.svelte.d.ts +0 -21
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* This page collects prompt data from the applicant. Only one route exists per
|
|
4
|
+
* prompt, even if the prompt is shared between multiple applications. We should
|
|
5
|
+
* be able to figure out which application we are in based on the full state of
|
|
6
|
+
* the app request since the prompt should only be visible in a single spot.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { Form, FormInlineNotification, confirmationStore } from '@txstate-mws/carbon-svelte'
|
|
10
|
+
import type { FormStore } from '@txstate-mws/svelte-forms'
|
|
11
|
+
import { Button, InlineNotification } from 'carbon-components-svelte'
|
|
12
|
+
import { getContext } from 'svelte'
|
|
13
|
+
import { get, type Writable } from 'svelte/store'
|
|
14
|
+
import { goto, invalidate } from '$app/navigation'
|
|
15
|
+
import type { ResolvedPathname } from '$app/types'
|
|
16
|
+
import { statusReasonsToFeedback, type StatusReason } from '@reqquest/ui'
|
|
17
|
+
import { uiRegistry } from '$local'
|
|
18
|
+
import { api } from '../api.js'
|
|
19
|
+
import type { PageData } from '../../routes/requests/[id]/apply/[promptId]/$types.js'
|
|
20
|
+
import ButtonLoadingIcon from './ButtonLoadingIcon.svelte'
|
|
21
|
+
import MissingDefinitionNotification from './MissingDefinitionNotification.svelte'
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
export let data: Awaited<PageData['applicantPromptPromise']> & { dataVersion: number }
|
|
25
|
+
export let statusReasons: StatusReason[] = []
|
|
26
|
+
$: ({ prompt, appRequest: appRequestForExport, dataVersion } = data)
|
|
27
|
+
$: def = uiRegistry.getPrompt(prompt.key)
|
|
28
|
+
$: statusFeedback = statusReasonsToFeedback(statusReasons, { blamedOnly: true, statuses: uiRegistry.statusReasonNotificationStatuses(prompt.key, 'applicant') })
|
|
29
|
+
const nextHref = getContext<Writable<{ nextHref: ResolvedPathname, prevHref: ResolvedPathname | undefined }>>('nextHref')
|
|
30
|
+
|
|
31
|
+
let store: FormStore | undefined
|
|
32
|
+
let continueAfterSave = false
|
|
33
|
+
$: hasPreviousPrompt = $nextHref.prevHref != null
|
|
34
|
+
|
|
35
|
+
async function handleBack () {
|
|
36
|
+
const previousHref = $nextHref.prevHref
|
|
37
|
+
if (previousHref) {
|
|
38
|
+
// eslint-disable-next-line svelte/no-navigation-without-resolve -- already resolved
|
|
39
|
+
await goto(previousHref)
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async function onSubmit (data: any) {
|
|
44
|
+
const { success, messages, data: newData } = await api.updatePrompt(prompt.id, data, false, dataVersion, await confirmInvalidatedOverride())
|
|
45
|
+
return {
|
|
46
|
+
success,
|
|
47
|
+
messages,
|
|
48
|
+
data: newData[prompt.key]
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async function onValidate (data: any) {
|
|
53
|
+
const { messages } = await api.updatePrompt(prompt.id, data, true, dataVersion)
|
|
54
|
+
return messages
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async function onSaved () {
|
|
58
|
+
if (continueAfterSave) {
|
|
59
|
+
// Bug issue that became apparent with loaders.
|
|
60
|
+
// only refresh nav/answered state, not this prompt page. read the store directly with
|
|
61
|
+
// get() rather than $nextHref/prompt.answered because its stale until reload reactive updates.
|
|
62
|
+
await invalidate('request:apply:nav')
|
|
63
|
+
// eslint-disable-next-line svelte/no-navigation-without-resolve -- already resolved
|
|
64
|
+
await goto(get(nextHref).nextHref)
|
|
65
|
+
} else {
|
|
66
|
+
await invalidate('request:apply')
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
async function confirmInvalidatedOverride() {
|
|
71
|
+
return (prompt.invalidated && !$store?.hasUnsavedChanges)
|
|
72
|
+
? await confirmationStore.confirm(
|
|
73
|
+
'Corrections are required, but no changes have been made. Can you confirm that all data is correct, and does not require changes?',
|
|
74
|
+
{
|
|
75
|
+
title: 'Confirm data is correct',
|
|
76
|
+
yesText: 'Yes',
|
|
77
|
+
noText: 'No'
|
|
78
|
+
}
|
|
79
|
+
)
|
|
80
|
+
: false
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
let lastPromptId: string | undefined
|
|
84
|
+
$: if (prompt.id !== lastPromptId) {
|
|
85
|
+
lastPromptId = prompt.id
|
|
86
|
+
store = undefined
|
|
87
|
+
}
|
|
88
|
+
</script>
|
|
89
|
+
|
|
90
|
+
{#key prompt.id}
|
|
91
|
+
<div class="prompt-intro flow max-w-screen-md mx-auto pt-10 px-6">
|
|
92
|
+
<!-- svelte-ignore a11y_autofocus -->
|
|
93
|
+
<h2 id="prompt-title" tabindex="-1" autofocus class="font-bold text-2xl leading-normal text-center">{prompt.title}</h2>
|
|
94
|
+
<p class="text-center"> {prompt.description}</p>
|
|
95
|
+
</div>
|
|
96
|
+
{#if def == null}
|
|
97
|
+
<div class="max-w-screen-md mx-auto px-6 pt-6">
|
|
98
|
+
<MissingDefinitionNotification kind="prompt" definitionKey={prompt.key} />
|
|
99
|
+
{#if hasPreviousPrompt}
|
|
100
|
+
<Button kind="ghost" on:click={handleBack}>Back</Button>
|
|
101
|
+
{/if}
|
|
102
|
+
</div>
|
|
103
|
+
{:else}
|
|
104
|
+
<Form class={def.applicantPromptPage?.formClass ?? uiRegistry.config.applicantPromptPage?.formClass} bind:store hideFallbackMessage unsavedWarning submit={onSubmit} validate={onValidate} preloadAsDraft={!prompt.hasSavedData} preload={prompt.preloadData} on:saved={onSaved} let:data>
|
|
105
|
+
<svelte:component this={def.formComponent} {data} appRequestId={appRequestForExport.id} appRequestData={appRequestForExport.data} prestageData={{latest: prompt.prestageData, current: appRequestForExport.data[prompt.key]?.__prestage}} fetched={prompt.fetchedData} configData={prompt.configurationData} gatheredConfigData={prompt.gatheredConfigData} invalidated={prompt.invalidated} invalidatedReason={prompt.invalidatedReason} {statusReasons} />
|
|
106
|
+
{#if statusFeedback.length}
|
|
107
|
+
<div class={def.applicantPromptPage?.statusReasonInlineNotificationClass ?? uiRegistry.config.applicantPromptPage?.statusReasonInlineNotificationClass}>
|
|
108
|
+
{#each statusFeedback as message}
|
|
109
|
+
<FormInlineNotification {message} />
|
|
110
|
+
{/each}
|
|
111
|
+
</div>
|
|
112
|
+
{/if}
|
|
113
|
+
<svelte:fragment slot="submit" let:submitting>
|
|
114
|
+
<div class='form-submit flex gap-12 justify-center mt-16'>
|
|
115
|
+
{#if hasPreviousPrompt}
|
|
116
|
+
<Button kind="ghost" on:click={handleBack}>Back</Button>
|
|
117
|
+
{/if}
|
|
118
|
+
<Button icon={submitting && !continueAfterSave ? ButtonLoadingIcon : null} type="submit" kind="secondary" disabled={submitting} on:click={() => { continueAfterSave = false }}>Save</Button>
|
|
119
|
+
<Button icon={submitting && !continueAfterSave ? ButtonLoadingIcon : null} type="submit" disabled={submitting} on:click={() => { continueAfterSave = true }}>Continue</Button>
|
|
120
|
+
</div>
|
|
121
|
+
</svelte:fragment>
|
|
122
|
+
<!-- Correction inline prompt notification -->
|
|
123
|
+
{#if prompt.invalidated && !$store?.hasUnsavedChanges}
|
|
124
|
+
<InlineNotification
|
|
125
|
+
class={def.applicantPromptPage?.invalidatedInlineNotificationClass ?? uiRegistry.config.applicantPromptPage?.invalidatedInlineNotificationClass}
|
|
126
|
+
kind="warning-alt"
|
|
127
|
+
title='Corrections needed'
|
|
128
|
+
hideCloseButton={true}
|
|
129
|
+
lowContrast
|
|
130
|
+
subtitle={prompt.invalidatedReason ?? 'Must update form data before continuing'}
|
|
131
|
+
/>
|
|
132
|
+
{/if}
|
|
133
|
+
</Form>
|
|
134
|
+
{/if}
|
|
135
|
+
{/key}
|
|
136
|
+
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { SkeletonText } from 'carbon-components-svelte'
|
|
3
|
+
</script>
|
|
4
|
+
<div class="gap-4 flex flex-col mt-10">
|
|
5
|
+
<SkeletonText />
|
|
6
|
+
<SkeletonText />
|
|
7
|
+
<SkeletonText />
|
|
8
|
+
<div style="width:100%" class="[ flex flex-row gap-20 justify-center ]">
|
|
9
|
+
<div class="still-skeleton bx--skeleton__text" style="height: 48px; width: 90px;" />
|
|
10
|
+
<div class="still-skeleton bx--skeleton__text" style="height: 48px; width: 90px;" />
|
|
11
|
+
</div>
|
|
12
|
+
</div>
|
|
@@ -0,0 +1,369 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { getAppRequestStatusInfo, getApplicationStatusInfo, applicantRequirementTypes, reviewRequirementTypes } from '../status-utils.js'
|
|
3
|
+
import { DEFAULT_APPLICANT_NO_PROGRAMS_MESSAGE, isHiddenFromApplicant, isIneligibleByApplicantRequirement } from '../appreq-utils.js'
|
|
4
|
+
import { Panel, TagSet } from '@txstate-mws/carbon-svelte'
|
|
5
|
+
import { Button, InlineNotification, Tooltip } from 'carbon-components-svelte'
|
|
6
|
+
import Edit from 'carbon-icons-svelte/lib/Edit.svelte'
|
|
7
|
+
import type { AnsweredPrompt, PromptSection, AppRequestForDetails, ApplicationForDetails, Scalars } from '@reqquest/ui'
|
|
8
|
+
import { enumRequirementType } from '@reqquest/ui'
|
|
9
|
+
import type { UIRegistry } from '@reqquest/ui'
|
|
10
|
+
import RenderDisplayComponent from './RenderDisplayComponent.svelte'
|
|
11
|
+
import ApplicantProgramList from './ApplicantProgramList.svelte'
|
|
12
|
+
import WarningIconYellow from './WarningIconYellow.svelte'
|
|
13
|
+
|
|
14
|
+
// TODO: design alignement could reduce props here
|
|
15
|
+
export let appRequest: AppRequestForDetails
|
|
16
|
+
export let applications: ApplicationForDetails[]
|
|
17
|
+
export let appData: Scalars['JsonData'] = {}
|
|
18
|
+
export let prequalPrompts: AnsweredPrompt[] | undefined = undefined
|
|
19
|
+
export let postqualPrompts: AnsweredPrompt[] | undefined = undefined
|
|
20
|
+
export let loading = false
|
|
21
|
+
export let uiRegistry: UIRegistry
|
|
22
|
+
export let title = `View your ${uiRegistry.getWord('appRequest').toLowerCase()}`
|
|
23
|
+
export let subtitle = 'Select document names to preview them.'
|
|
24
|
+
export let expandable = true
|
|
25
|
+
export let showWarningsInline = false
|
|
26
|
+
export let showCorrectionsInline = false
|
|
27
|
+
export let showAppRequestStatus = true
|
|
28
|
+
export let statusDisplay: 'tags' | 'icons' = 'tags'
|
|
29
|
+
export let showTooltipsAsText = false
|
|
30
|
+
/** Apply print-export styles: hide app chrome and paginate by prompt row rather than by panel. */
|
|
31
|
+
export let printMode = false
|
|
32
|
+
|
|
33
|
+
const CORRECTABLE_STATUSES = ['STARTED', 'READY_TO_SUBMIT', 'DISQUALIFIED']
|
|
34
|
+
|
|
35
|
+
$: canMakeCorrections = CORRECTABLE_STATUSES.includes(appRequest.status)
|
|
36
|
+
$: eligibleApplications = applications.filter(a => a.ineligiblePhase == null)
|
|
37
|
+
$: ineligibleApplications = applications.filter(a => isIneligibleByApplicantRequirement(a) && !isHiddenFromApplicant(a))
|
|
38
|
+
$: noProgramsToList = !eligibleApplications.length && !ineligibleApplications.length && applications.some(isHiddenFromApplicant)
|
|
39
|
+
|
|
40
|
+
// Group prompts by sections, with reviewer prompts nested within application sections
|
|
41
|
+
$: sections = (() => {
|
|
42
|
+
const sections: PromptSection[] = []
|
|
43
|
+
|
|
44
|
+
// General Questions === PREQUAL prompts
|
|
45
|
+
if (prequalPrompts?.length) {
|
|
46
|
+
sections.push({ title: 'Eligibility screening questions', prompts: prequalPrompts })
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Application-Specific Questions with nested Reviewer Questions
|
|
50
|
+
for (const application of applications) {
|
|
51
|
+
const applicantPrompts = application.requirements
|
|
52
|
+
.filter(r => applicantRequirementTypes.has(r.type))
|
|
53
|
+
.flatMap(r => r.prompts).filter(p => !p.optOut)
|
|
54
|
+
const reviewerPrompts = application.requirements
|
|
55
|
+
.filter(r => reviewRequirementTypes.has(r.type))
|
|
56
|
+
.flatMap(r => r.prompts)
|
|
57
|
+
const workflowPrompts = application.requirements
|
|
58
|
+
.filter(r => r.type === enumRequirementType.WORKFLOW)
|
|
59
|
+
.flatMap(r => r.prompts)
|
|
60
|
+
|
|
61
|
+
if (applicantPrompts.length || reviewerPrompts.length || workflowPrompts.length) {
|
|
62
|
+
const subsections: PromptSection[] = []
|
|
63
|
+
if (reviewerPrompts.length) subsections.push({ title: 'Reviewer Questions', prompts: reviewerPrompts })
|
|
64
|
+
if (workflowPrompts.length) subsections.push({ title: 'Workflow Questions', prompts: workflowPrompts })
|
|
65
|
+
sections.push({
|
|
66
|
+
title: application.title,
|
|
67
|
+
prompts: applicantPrompts,
|
|
68
|
+
subsections,
|
|
69
|
+
applicationStatus: application.status,
|
|
70
|
+
applicationRescindedStatus: application.rescindedStatus
|
|
71
|
+
})
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Additional Questions === POSTQUAL prompts
|
|
76
|
+
if (postqualPrompts?.length) {
|
|
77
|
+
sections.push({ title: 'Additional Questions', prompts: postqualPrompts })
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return sections
|
|
81
|
+
})()
|
|
82
|
+
|
|
83
|
+
function hasWarning (prompt: AnsweredPrompt) {
|
|
84
|
+
return prompt.statusReasons.some(r => r.status === 'WARNING' || r.status === 'DISQUALIFYING')
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function getWarnings (prompt: AnsweredPrompt) {
|
|
88
|
+
return prompt.statusReasons.filter(r => r.status === 'WARNING' || r.status === 'DISQUALIFYING')
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function needsCorrection (prompt: AnsweredPrompt) {
|
|
92
|
+
return prompt.invalidated && prompt.invalidatedReason
|
|
93
|
+
}
|
|
94
|
+
</script>
|
|
95
|
+
|
|
96
|
+
{#if appRequest}
|
|
97
|
+
<div class="[ my-6 ] application-details flow" class:print-mode={printMode}>
|
|
98
|
+
<!-- Application Status Panel -->
|
|
99
|
+
<header class="[ mb-12 ] app-view-intro text-center">
|
|
100
|
+
<h2 class="[ text-2xl mb-3 font-bold ]">{title}</h2>
|
|
101
|
+
<p class="app-view-subtitle mb-3">{subtitle}</p>
|
|
102
|
+
</header>
|
|
103
|
+
<section class="prompt-section">
|
|
104
|
+
<div class="status-content">
|
|
105
|
+
{#if showAppRequestStatus}
|
|
106
|
+
<dl class="status-list-item [ flex items-center justify-between px-2 py-3 border-b ]">
|
|
107
|
+
<dt class="status-list-label font-medium">{uiRegistry.getWord('appRequest')} Status</dt>
|
|
108
|
+
<dd class="px-2">
|
|
109
|
+
<TagSet tags={getAppRequestStatusInfo(appRequest.status, appRequest.phase, appRequest.closedAt).tags} />
|
|
110
|
+
</dd>
|
|
111
|
+
</dl>
|
|
112
|
+
{/if}
|
|
113
|
+
|
|
114
|
+
<!-- Application Status List -->
|
|
115
|
+
{#if eligibleApplications.length > 0}
|
|
116
|
+
<ApplicantProgramList applications={eligibleApplications} {appRequest} viewMode={statusDisplay === 'tags'} {showTooltipsAsText} />
|
|
117
|
+
{/if}
|
|
118
|
+
<!-- Ineligible Status List -->
|
|
119
|
+
{#if ineligibleApplications.length > 0}
|
|
120
|
+
<Panel title="Ineligible benefits" expandable={true} expanded={(eligibleApplications.length === 0)}>
|
|
121
|
+
<ApplicantProgramList applications={ineligibleApplications} {appRequest} viewMode={statusDisplay === 'tags'} {showTooltipsAsText} />
|
|
122
|
+
</Panel>
|
|
123
|
+
{/if}
|
|
124
|
+
{#if noProgramsToList}
|
|
125
|
+
<p class="text-center">{uiRegistry.config.applicantNoProgramsMessage ?? DEFAULT_APPLICANT_NO_PROGRAMS_MESSAGE}</p>
|
|
126
|
+
{/if}
|
|
127
|
+
</div>
|
|
128
|
+
</section>
|
|
129
|
+
|
|
130
|
+
<!-- Prompt Sections -->
|
|
131
|
+
{#if loading}
|
|
132
|
+
<Panel title="Loading...">
|
|
133
|
+
<p>Loading prompt data...</p>
|
|
134
|
+
</Panel>
|
|
135
|
+
{:else if sections.length > 0}
|
|
136
|
+
{#each sections as section (section.title)}
|
|
137
|
+
{@const applicationStatusInfo = section.applicationStatus ? getApplicationStatusInfo(section.applicationStatus, appRequest.phase, appRequest.closedAt, section.applicationRescindedStatus) : undefined}
|
|
138
|
+
<Panel title={section.title} {expandable} expanded>
|
|
139
|
+
{#if section.prompts.length}
|
|
140
|
+
<dl class="prompt-list">
|
|
141
|
+
{#each section.prompts as prompt (prompt.id)}
|
|
142
|
+
{@const def = uiRegistry.getPrompt(prompt.key)}
|
|
143
|
+
<dt class="prompt-term [ font-medium ]">
|
|
144
|
+
{#if showWarningsInline && hasWarning(prompt)}
|
|
145
|
+
<Tooltip align="start">
|
|
146
|
+
<div class="icon" slot="icon">
|
|
147
|
+
<WarningIconYellow size={16} />
|
|
148
|
+
</div>
|
|
149
|
+
{#each getWarnings(prompt) as r, i (i)}
|
|
150
|
+
<p>
|
|
151
|
+
{r.status === 'WARNING' ? 'Warning' : 'Disqualifying'}{#if section.title !== r.programName} for {r.programName}{/if}<br>
|
|
152
|
+
{r.statusReason}
|
|
153
|
+
</p>
|
|
154
|
+
{/each}
|
|
155
|
+
</Tooltip>
|
|
156
|
+
{/if}
|
|
157
|
+
{#if showCorrectionsInline && canMakeCorrections && needsCorrection(prompt)}
|
|
158
|
+
<Tooltip align="start">
|
|
159
|
+
<div class="icon" slot="icon">
|
|
160
|
+
<WarningIconYellow size={16} />
|
|
161
|
+
</div>
|
|
162
|
+
<p>Corrections needed<br>{prompt.invalidatedReason}</p>
|
|
163
|
+
</Tooltip>
|
|
164
|
+
{/if}
|
|
165
|
+
{prompt.title}
|
|
166
|
+
</dt>
|
|
167
|
+
<dd class="prompt-answer flow" class:large={def?.displayMode === 'large'}>
|
|
168
|
+
<RenderDisplayComponent {def} appRequestId={appRequest.id} appData={appData} prompt={prompt} prestageData={{latest: prompt.prestageData, current: appRequest.data[prompt.key]?.__prestage}} configData={prompt.configurationData} gatheredConfigData={prompt.gatheredConfigData} statusReasons={prompt.statusReasons} />
|
|
169
|
+
{#if showCorrectionsInline && canMakeCorrections && needsCorrection(prompt)}
|
|
170
|
+
<div class="correction-notice">
|
|
171
|
+
<Button kind="ghost" size="small" icon={Edit} iconDescription="Edit this answer" href={`/requests/${appRequest.id}/apply/${prompt.id}`} class="edit-button" />
|
|
172
|
+
<InlineNotification kind="warning-alt" title="Corrections needed" subtitle={prompt.invalidatedReason ?? ''} hideCloseButton lowContrast />
|
|
173
|
+
</div>
|
|
174
|
+
{/if}
|
|
175
|
+
</dd>
|
|
176
|
+
{/each}
|
|
177
|
+
</dl>
|
|
178
|
+
{/if}
|
|
179
|
+
|
|
180
|
+
<!-- Nested subsections (e.g., Reviewer Questions) -->
|
|
181
|
+
{#if section.subsections}
|
|
182
|
+
{#each section.subsections as subsection (subsection.title)}
|
|
183
|
+
<Panel title={subsection.title} {expandable} expanded>
|
|
184
|
+
<dl class="prompt-list">
|
|
185
|
+
{#each subsection.prompts as prompt (prompt.id)}
|
|
186
|
+
{@const def = uiRegistry.getPrompt(prompt.key)}
|
|
187
|
+
<dt class="prompt-term [ font-medium ]">
|
|
188
|
+
{#if showWarningsInline && hasWarning(prompt)}
|
|
189
|
+
<Tooltip align="start">
|
|
190
|
+
<div class="icon" slot="icon">
|
|
191
|
+
<WarningIconYellow size={16} />
|
|
192
|
+
</div>
|
|
193
|
+
{#each getWarnings(prompt) as r, i (i)}
|
|
194
|
+
<p>
|
|
195
|
+
{r.status === 'WARNING' ? 'Warning' : 'Disqualifying'}{#if section.title !== r.programName} for {r.programName}{/if}<br>
|
|
196
|
+
{r.statusReason}
|
|
197
|
+
</p>
|
|
198
|
+
{/each}
|
|
199
|
+
</Tooltip>
|
|
200
|
+
{/if}
|
|
201
|
+
{#if showCorrectionsInline && canMakeCorrections && needsCorrection(prompt)}
|
|
202
|
+
<Tooltip align="start">
|
|
203
|
+
<div class="icon" slot="icon">
|
|
204
|
+
<WarningIconYellow size={16} />
|
|
205
|
+
</div>
|
|
206
|
+
<p>Corrections needed<br>{prompt.invalidatedReason}</p>
|
|
207
|
+
</Tooltip>
|
|
208
|
+
{/if}
|
|
209
|
+
{prompt.title}
|
|
210
|
+
</dt>
|
|
211
|
+
<dd class="prompt-answer flow" class:large={def?.displayMode === 'large'}>
|
|
212
|
+
<RenderDisplayComponent {def} appRequestId={appRequest.id} appData={appData} prompt={prompt} prestageData={{latest: prompt.prestageData, current: appRequest.data[prompt.key]?.__prestage}} configData={prompt.configurationData} gatheredConfigData={prompt.gatheredConfigData} statusReasons={prompt.statusReasons} />
|
|
213
|
+
{#if showCorrectionsInline && canMakeCorrections && needsCorrection(prompt)}
|
|
214
|
+
<Button kind="ghost" size="small" icon={Edit} iconDescription="Edit this answer" href={`/requests/${appRequest.id}/apply/${prompt.id}`} class="edit-button" />
|
|
215
|
+
{/if}
|
|
216
|
+
</dd>
|
|
217
|
+
{#if showCorrectionsInline && canMakeCorrections && needsCorrection(prompt)}
|
|
218
|
+
<div class="correction-notice">
|
|
219
|
+
<InlineNotification kind="warning-alt" title="Corrections needed" subtitle={prompt.invalidatedReason ?? ''} hideCloseButton lowContrast />
|
|
220
|
+
</div>
|
|
221
|
+
{/if}
|
|
222
|
+
{/each}
|
|
223
|
+
</dl>
|
|
224
|
+
</Panel>
|
|
225
|
+
{/each}
|
|
226
|
+
{/if}
|
|
227
|
+
</Panel>
|
|
228
|
+
{/each}
|
|
229
|
+
{:else}
|
|
230
|
+
<Panel title="Prompts and Answers">
|
|
231
|
+
<p>No prompts available for this {uiRegistry.getWord('appRequest').toLowerCase()}.</p>
|
|
232
|
+
</Panel>
|
|
233
|
+
{/if}
|
|
234
|
+
|
|
235
|
+
<slot name="footer" />
|
|
236
|
+
</div>
|
|
237
|
+
{:else}
|
|
238
|
+
<p>No {uiRegistry.getWord('appRequest').toLowerCase()} selected</p>
|
|
239
|
+
{/if}
|
|
240
|
+
|
|
241
|
+
<style>
|
|
242
|
+
.app-view-subtitle {
|
|
243
|
+
color: var(--cds-text-02);
|
|
244
|
+
font-size: 0.9rem;
|
|
245
|
+
/* margin-top: -0.5rem; */
|
|
246
|
+
margin-bottom: 1rem;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
.status-list-item {
|
|
250
|
+
border-bottom: 1px solid var(--cds-border-subtle);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
.prompt-answer :global(dl:not(.prompt-display-grid)) {
|
|
254
|
+
padding-block-start:1em;
|
|
255
|
+
display:grid;
|
|
256
|
+
grid-template-columns: 1fr 1fr;
|
|
257
|
+
row-gap:0.5rem;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
.status-content dl {
|
|
261
|
+
padding-block-start:1em;
|
|
262
|
+
display:grid;
|
|
263
|
+
grid-template-columns: 1fr 1fr;
|
|
264
|
+
row-gap:0.5rem;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
.status-content :global(.programs-container) {
|
|
268
|
+
grid-template-columns: 1fr 1fr;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
.status-list-item {
|
|
272
|
+
border-color: var(--cds-border-subtle);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
.status-list-label {
|
|
276
|
+
color: var(--cds-text-01);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
.prompt-list {
|
|
280
|
+
display: grid;
|
|
281
|
+
grid-template-columns: var(--prompt-list-columns, 1fr 1fr);
|
|
282
|
+
align-items: stretch;
|
|
283
|
+
row-gap: 0.5rem;
|
|
284
|
+
background-color: #F2F2F2;
|
|
285
|
+
padding-top: 0.5rem;
|
|
286
|
+
}
|
|
287
|
+
.prompt-list dt, .prompt-list dd {
|
|
288
|
+
padding-top: 0.25rem;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
.prompt-term {
|
|
292
|
+
display: flex;
|
|
293
|
+
gap:1em;
|
|
294
|
+
color: var(--cds-text-01);
|
|
295
|
+
border-bottom: 1px solid var(--cds-border-subtle);
|
|
296
|
+
padding: 0.5rem 10px;
|
|
297
|
+
}
|
|
298
|
+
.prompt-term:has(+ .prompt-answer.large) {
|
|
299
|
+
border-bottom: none;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
.prompt-answer {
|
|
303
|
+
color: var(--cds-text-02);
|
|
304
|
+
border-bottom: 1px solid var(--cds-border-subtle);
|
|
305
|
+
padding: 0.5rem 10px;
|
|
306
|
+
}
|
|
307
|
+
.prompt-answer.large {
|
|
308
|
+
grid-column: span 2;
|
|
309
|
+
}
|
|
310
|
+
/* When a display component's root is a PromptDisplayGrid, hand it our column tracks
|
|
311
|
+
via subgrid so its challenge/response rows align with the real prompt rows. */
|
|
312
|
+
.prompt-answer.large:has(> :global(.prompt-display-grid)) {
|
|
313
|
+
display: grid;
|
|
314
|
+
grid-template-columns: subgrid;
|
|
315
|
+
align-content: start;
|
|
316
|
+
padding: 0;
|
|
317
|
+
border-bottom: none;
|
|
318
|
+
--prompt-display-subgrid: subgrid;
|
|
319
|
+
--prompt-display-columns: var(--prompt-list-columns, 1fr 1fr);
|
|
320
|
+
--prompt-display-row-padding: 0.5rem 10px;
|
|
321
|
+
--prompt-display-challenge-padding-left: 10px;
|
|
322
|
+
--prompt-display-row-gap: 0.5rem;
|
|
323
|
+
}
|
|
324
|
+
.prompt-answer.large:has(> :global(.prompt-display-grid)) > :global(*) {
|
|
325
|
+
grid-column: 1 / -1;
|
|
326
|
+
}
|
|
327
|
+
.prompt-answer.large :global(.prompt-display-challenge) {
|
|
328
|
+
font-weight: 500;
|
|
329
|
+
color: var(--cds-text-01);
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
.correction-notice {
|
|
333
|
+
grid-column: span 2;
|
|
334
|
+
margin-bottom: 0.5rem;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
.correction-notice :global(.bx--inline-notification) {
|
|
338
|
+
max-width: 100%;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
.prompt-answer :global(.edit-button) {
|
|
342
|
+
float: right;
|
|
343
|
+
margin-top: -0.5rem;
|
|
344
|
+
--cds-icon-01: var(--cds-link-01);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
@media print {
|
|
348
|
+
:global(body:has(.application-details.print-mode) .bx--header),
|
|
349
|
+
:global(body:has(.application-details.print-mode) .breadcrumbs) {
|
|
350
|
+
display: none !important;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
/* Never mark the whole view or a whole panel break-inside: avoid — when either is
|
|
354
|
+
taller than the space left on a page, the browser pushes it to the next page and
|
|
355
|
+
leaves the first page nearly blank. Keep only small units together instead. */
|
|
356
|
+
.print-mode :global(.panel) {
|
|
357
|
+
margin-bottom: 1rem !important;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
.print-mode :global(.panel-header) {
|
|
361
|
+
break-after: avoid;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
.print-mode .prompt-term,
|
|
365
|
+
.print-mode .prompt-answer:not(.large) {
|
|
366
|
+
break-inside: avoid;
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
</style>
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { ScreenReaderOnly } from '@txstate-mws/svelte-components'
|
|
3
|
+
</script>
|
|
4
|
+
|
|
5
|
+
<svg style:display="none"></svg>
|
|
6
|
+
<span {...$$restProps} class="{$$restProps.class} loader" aria-hidden="false"><ScreenReaderOnly>loading</ScreenReaderOnly></span>
|
|
7
|
+
|
|
8
|
+
<style>
|
|
9
|
+
.loader {
|
|
10
|
+
width: 16px;
|
|
11
|
+
height: 16px;
|
|
12
|
+
border: 3px solid #FFF;
|
|
13
|
+
border-bottom-color: transparent;
|
|
14
|
+
border-radius: 50%;
|
|
15
|
+
display: inline-block;
|
|
16
|
+
box-sizing: border-box;
|
|
17
|
+
animation: rotation 1s linear infinite;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
@keyframes rotation {
|
|
21
|
+
0% {
|
|
22
|
+
transform: rotate(0deg);
|
|
23
|
+
}
|
|
24
|
+
100% {
|
|
25
|
+
transform: rotate(360deg);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
</style>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import type { Loader } from "
|
|
3
|
-
import ApplicantPromptSkeleton from "
|
|
2
|
+
import type { Loader } from "@reqquest/ui"
|
|
3
|
+
import ApplicantPromptSkeleton from "$internal/components/ApplicantPromptSkeleton.svelte"
|
|
4
4
|
|
|
5
5
|
export let loader: Loader
|
|
6
6
|
|