@reqquest/ui 1.17.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/package.json +29 -15
- package/dist/components/DelayedSkeleton.svelte.d.ts +0 -21
|
@@ -0,0 +1,1707 @@
|
|
|
1
|
+
import { PUBLIC_API_BASE, PUBLIC_AUTH_REDIRECT, PUBLIC_SHOW_DUPLICATE_PROMPTS } from '$env/static/public'
|
|
2
|
+
import { error } from '@sveltejs/kit'
|
|
3
|
+
import { APIBase, type MutationResponseFromAPI } from '@txstate-mws/sveltekit-utils'
|
|
4
|
+
import type { Feedback } from '@txstate-mws/svelte-forms'
|
|
5
|
+
import { DateTime } from 'luxon'
|
|
6
|
+
import { keyby, omit, pick } from 'txstate-utils'
|
|
7
|
+
import {
|
|
8
|
+
createClient, enumAppRequestIndexDestination, enumIneligiblePhases, enumPromptVisibility, enumRequirementStatus, enumRequirementType,
|
|
9
|
+
type AccessRoleGrantCreate, type AccessRoleGrantUpdate, type AccessRoleGroup, type AccessRoleInput, type AccessUserFilter,
|
|
10
|
+
type AppRequestActivityFilters, type AppRequestFilter, type IneligiblePhases, type Pagination, type PeriodUpdate, type PromptVisibility,
|
|
11
|
+
type RequirementStatus, type RequirementType, type PhaseChangeMutations, type CompletionStatus, type StatusReason,
|
|
12
|
+
type PeriodFilters
|
|
13
|
+
} from '@reqquest/ui'
|
|
14
|
+
import { applicantVisiblePromptVisibilities } from './status-utils.js'
|
|
15
|
+
|
|
16
|
+
export const showDupePrompts = PUBLIC_SHOW_DUPLICATE_PROMPTS.trim() === 'true'
|
|
17
|
+
// the per-key configuration fields attached to prompt instances client-side, see API.attachPromptConfigs
|
|
18
|
+
export type PromptConfig = { configurationData: Record<string, any>, gatheredConfigData: Record<string, any> }
|
|
19
|
+
export type DashboardAppRequest = Awaited<ReturnType<typeof api.getApplicantRequests>>[number]
|
|
20
|
+
export type AppRequestForExportResponse = Awaited<ReturnType<typeof api.getAppRequestForExport>>
|
|
21
|
+
export type PromptForEditing = Awaited<ReturnType<typeof api.getApplicantPrompt>>['prompt']
|
|
22
|
+
export type ReviewData = Awaited<ReturnType<typeof api.getReviewData>>
|
|
23
|
+
export type PromptDataLegion = Awaited<ReturnType<typeof api.getPromptDataLegion>>
|
|
24
|
+
export type BasicRequestData = Awaited<ReturnType<typeof api.getBasicRequestData>>
|
|
25
|
+
|
|
26
|
+
class API extends APIBase {
|
|
27
|
+
baseUrl = PUBLIC_API_BASE
|
|
28
|
+
client = createClient({
|
|
29
|
+
url: this.baseUrl,
|
|
30
|
+
fetcher: async operation => {
|
|
31
|
+
if (Array.isArray(operation)) throw new Error('Batching not supported')
|
|
32
|
+
const data = await this.graphql(operation.query, operation.variables)
|
|
33
|
+
return { data }
|
|
34
|
+
}
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
async getDownloadTicket () {
|
|
38
|
+
const resp = await this.post<{ ticket: string }>('/ticket')
|
|
39
|
+
return resp.ticket
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async getAccess () {
|
|
43
|
+
const response = await this.client.query({
|
|
44
|
+
__name: 'GetAccess',
|
|
45
|
+
access: {
|
|
46
|
+
user: {
|
|
47
|
+
login: true,
|
|
48
|
+
fullname: true
|
|
49
|
+
},
|
|
50
|
+
createPeriod: true,
|
|
51
|
+
createRole: true,
|
|
52
|
+
viewRoleManagement: true,
|
|
53
|
+
viewPeriodManagement: true,
|
|
54
|
+
viewReviewerInterface: true,
|
|
55
|
+
viewApplicantDashboard: true,
|
|
56
|
+
viewAppRequestList: true,
|
|
57
|
+
createAppRequestSelf: true,
|
|
58
|
+
createAppRequestOther: true,
|
|
59
|
+
viewAnnouncementManagement: true,
|
|
60
|
+
manageAnnouncements: true,
|
|
61
|
+
viewMetrics: true
|
|
62
|
+
}
|
|
63
|
+
})
|
|
64
|
+
return response.access
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
async getAccessUser (login: string) {
|
|
68
|
+
const filter: AccessUserFilter = { logins: [login] }
|
|
69
|
+
const response = await this.client.query({
|
|
70
|
+
__name: 'GetAccessUser',
|
|
71
|
+
accessUsers: {
|
|
72
|
+
__args: { filter },
|
|
73
|
+
login: true,
|
|
74
|
+
fullname: true,
|
|
75
|
+
otherInfo: true
|
|
76
|
+
}
|
|
77
|
+
})
|
|
78
|
+
return response.accessUsers[0]
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
async getAccessUsers (accessUsersFilter: AccessUserFilter, pageFilter: Pagination) {
|
|
82
|
+
const filter = accessUsersFilter
|
|
83
|
+
const paged = pageFilter
|
|
84
|
+
const response = await this.client.query({
|
|
85
|
+
__name: 'GetAccessUsers',
|
|
86
|
+
accessUsers: {
|
|
87
|
+
__args: { filter, paged },
|
|
88
|
+
login: true,
|
|
89
|
+
fullname: true,
|
|
90
|
+
groups: true,
|
|
91
|
+
roles: {
|
|
92
|
+
name: true
|
|
93
|
+
},
|
|
94
|
+
otherIdentifiers: {
|
|
95
|
+
id: true,
|
|
96
|
+
label: true
|
|
97
|
+
},
|
|
98
|
+
otherInfo: true
|
|
99
|
+
},
|
|
100
|
+
pageInfo: {
|
|
101
|
+
accessUsers: {
|
|
102
|
+
currentPage: true,
|
|
103
|
+
totalItems: true,
|
|
104
|
+
hasNextPage: true,
|
|
105
|
+
perPage: true,
|
|
106
|
+
categories: {
|
|
107
|
+
tags: {
|
|
108
|
+
tag: true,
|
|
109
|
+
label: true
|
|
110
|
+
},
|
|
111
|
+
category: true,
|
|
112
|
+
label: true,
|
|
113
|
+
useInFilters: true,
|
|
114
|
+
useInList: true
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
})
|
|
119
|
+
return { users: response.accessUsers, pageInfo: response.pageInfo.accessUsers }
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
async getApplicantRequests (additionalFilters: AppRequestFilter = {}) {
|
|
123
|
+
const filter: AppRequestFilter = { ...additionalFilters }
|
|
124
|
+
const response = await this.client.query({
|
|
125
|
+
__name: 'GetApplicantRequests',
|
|
126
|
+
appRequests: {
|
|
127
|
+
__args: { filter },
|
|
128
|
+
id: true,
|
|
129
|
+
status: true,
|
|
130
|
+
phase: true,
|
|
131
|
+
createdAt: true,
|
|
132
|
+
updatedAt: true,
|
|
133
|
+
closedAt: true,
|
|
134
|
+
applicant: {
|
|
135
|
+
login: true,
|
|
136
|
+
fullname: true,
|
|
137
|
+
otherIdentifiers: {
|
|
138
|
+
id: true
|
|
139
|
+
}
|
|
140
|
+
},
|
|
141
|
+
period: {
|
|
142
|
+
id: true,
|
|
143
|
+
name: true,
|
|
144
|
+
openDate: true,
|
|
145
|
+
closeDate: true
|
|
146
|
+
},
|
|
147
|
+
applications: {
|
|
148
|
+
id: true,
|
|
149
|
+
title: true,
|
|
150
|
+
status: true,
|
|
151
|
+
rescindedStatus: true,
|
|
152
|
+
statusReason: true,
|
|
153
|
+
ineligiblePhase: true,
|
|
154
|
+
requirements: {
|
|
155
|
+
id: true,
|
|
156
|
+
type: true,
|
|
157
|
+
status: true,
|
|
158
|
+
statusReason: true,
|
|
159
|
+
prompts: {
|
|
160
|
+
id: true,
|
|
161
|
+
visibility: true,
|
|
162
|
+
moot: true,
|
|
163
|
+
invalidated: true,
|
|
164
|
+
invalidatedReason: true
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
},
|
|
168
|
+
actions: {
|
|
169
|
+
acceptOffer: true,
|
|
170
|
+
cancel: true,
|
|
171
|
+
reopen: true,
|
|
172
|
+
returnToApplicant: true,
|
|
173
|
+
returnToOffer: true,
|
|
174
|
+
submit: true
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
})
|
|
178
|
+
return response.appRequests
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
async getReviewerDashboardRequests (filter: AppRequestFilter = {}, paged?: Pagination) {
|
|
182
|
+
const response = await this.client.query({
|
|
183
|
+
__name: 'GetReviewerDashboardRequests',
|
|
184
|
+
appRequestIndexes: {
|
|
185
|
+
__args: { for: enumAppRequestIndexDestination.REVIEWER_DASHBOARD },
|
|
186
|
+
category: true,
|
|
187
|
+
categoryLabel: true,
|
|
188
|
+
reviewerDashboardPriority: true,
|
|
189
|
+
listable: true,
|
|
190
|
+
values: {
|
|
191
|
+
value: true,
|
|
192
|
+
label: true
|
|
193
|
+
}
|
|
194
|
+
},
|
|
195
|
+
appRequests: {
|
|
196
|
+
__args: { filter, paged },
|
|
197
|
+
id: true,
|
|
198
|
+
phase: true,
|
|
199
|
+
status: true,
|
|
200
|
+
createdAt: true,
|
|
201
|
+
updatedAt: true,
|
|
202
|
+
closedAt: true,
|
|
203
|
+
indexCategories: {
|
|
204
|
+
__args: { for: enumAppRequestIndexDestination.REVIEWER_DASHBOARD },
|
|
205
|
+
categoryLabel: true,
|
|
206
|
+
values: {
|
|
207
|
+
label: true
|
|
208
|
+
}
|
|
209
|
+
},
|
|
210
|
+
applicant: {
|
|
211
|
+
login: true,
|
|
212
|
+
fullname: true,
|
|
213
|
+
otherIdentifiers: {
|
|
214
|
+
id: true
|
|
215
|
+
}
|
|
216
|
+
},
|
|
217
|
+
period: {
|
|
218
|
+
name: true
|
|
219
|
+
},
|
|
220
|
+
applications: {
|
|
221
|
+
id: true,
|
|
222
|
+
title: true,
|
|
223
|
+
programKey: true,
|
|
224
|
+
status: true,
|
|
225
|
+
rescindedStatus: true
|
|
226
|
+
},
|
|
227
|
+
actions: {
|
|
228
|
+
review: true
|
|
229
|
+
}
|
|
230
|
+
},
|
|
231
|
+
pageInfo: {
|
|
232
|
+
appRequests: {
|
|
233
|
+
totalItems: true
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
})
|
|
237
|
+
|
|
238
|
+
return response
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/** First-time vs returning applicant counts (an applicant's first request in a period is first-time). */
|
|
242
|
+
async getAppRequestApplicantCounts (filter: AppRequestFilter = {}) {
|
|
243
|
+
const response = await this.client.query({
|
|
244
|
+
__name: 'GetAppRequestApplicantCounts',
|
|
245
|
+
countAppRequestApplicants: {
|
|
246
|
+
__args: { filter },
|
|
247
|
+
firstTime: true,
|
|
248
|
+
returning: true
|
|
249
|
+
}
|
|
250
|
+
})
|
|
251
|
+
return response.countAppRequestApplicants
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/** Average seconds from submission to decision across applications, from the Metrics API. Requires access.viewMetrics. */
|
|
255
|
+
async getReviewDecisionTiming () {
|
|
256
|
+
const response = await this.client.query({
|
|
257
|
+
__name: 'GetReviewDecisionTiming',
|
|
258
|
+
applicationMetrics: {
|
|
259
|
+
toDecision: {
|
|
260
|
+
avg: true
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
})
|
|
264
|
+
return response.applicationMetrics.toDecision.avg ?? null
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
async getPrograms () {
|
|
268
|
+
const response = await this.client.query({
|
|
269
|
+
__name: 'GetPrograms',
|
|
270
|
+
programs: {
|
|
271
|
+
key: true,
|
|
272
|
+
title: true
|
|
273
|
+
}
|
|
274
|
+
})
|
|
275
|
+
return response.programs
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
async getApplicationCount (filter: AppRequestFilter = {}) {
|
|
279
|
+
const response = await this.client.query({
|
|
280
|
+
__name: 'GetApplicationCount',
|
|
281
|
+
countAppRequests: {
|
|
282
|
+
__args: { filter }
|
|
283
|
+
}
|
|
284
|
+
})
|
|
285
|
+
return response.countAppRequests
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
async getApplicantPrompt (appRequestId: string, promptId: string) {
|
|
289
|
+
const response = await this.client.query({
|
|
290
|
+
__name: 'GetApplicantPrompt',
|
|
291
|
+
appRequests: {
|
|
292
|
+
__args: { filter: { ids: [appRequestId] } },
|
|
293
|
+
data: true,
|
|
294
|
+
dataVersion: true,
|
|
295
|
+
prompt: {
|
|
296
|
+
__args: { promptId },
|
|
297
|
+
id: true,
|
|
298
|
+
key: true,
|
|
299
|
+
title: true,
|
|
300
|
+
description: true,
|
|
301
|
+
answered: true,
|
|
302
|
+
visibility: true,
|
|
303
|
+
hasSavedData: true,
|
|
304
|
+
preloadData: true,
|
|
305
|
+
prestageData: true,
|
|
306
|
+
fetchedData: true,
|
|
307
|
+
configurationData: true,
|
|
308
|
+
gatheredConfigData: true,
|
|
309
|
+
invalidated: true,
|
|
310
|
+
invalidatedReason: true
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
})
|
|
314
|
+
return { appRequest: response.appRequests[0], prompt: response.appRequests[0].prompt }
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
async stagePrompt (promptId: string, dataVersion?: number) {
|
|
318
|
+
const response = await this.graphqlWithUploads<{ stagePrompt: MutationResponseFromAPI }>(`
|
|
319
|
+
mutation StagePrompt($promptId: ID!, $dataVersion: Int) {
|
|
320
|
+
stagePrompt(promptId: $promptId, dataVersion: $dataVersion) {
|
|
321
|
+
success
|
|
322
|
+
messages {
|
|
323
|
+
message
|
|
324
|
+
type
|
|
325
|
+
arg
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
`, { promptId, dataVersion })
|
|
330
|
+
return this.mutationForDialog(response.stagePrompt)
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
async updatePrompt (promptId: string, data: any, validateOnly: boolean, dataVersion?: number, overrideInvalidated?: boolean) {
|
|
334
|
+
const response = await this.graphqlWithUploads<{ updatePrompt: MutationResponseFromAPI }>(`
|
|
335
|
+
mutation UpdatePrompt($promptId: ID!, $data: JsonData!, $validateOnly: Boolean!, $dataVersion: Int, $overrideInvalidated: Boolean) {
|
|
336
|
+
updatePrompt(promptId: $promptId, data: $data, validateOnly: $validateOnly, dataVersion: $dataVersion, overrideInvalidated: $overrideInvalidated) {
|
|
337
|
+
success
|
|
338
|
+
messages {
|
|
339
|
+
message
|
|
340
|
+
type
|
|
341
|
+
arg
|
|
342
|
+
}
|
|
343
|
+
appRequest {
|
|
344
|
+
data
|
|
345
|
+
dataVersion
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
`, { promptId, data, validateOnly, dataVersion, overrideInvalidated })
|
|
350
|
+
return { ...this.mutationForDialog(response.updatePrompt), data: response.updatePrompt.appRequest.data, dataVersion: response.updatePrompt.appRequest.dataVersion as number | undefined }
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
async updateConfiguration (periodId: string, definitionKey: string, data: any, validateOnly: boolean) {
|
|
354
|
+
const response = await this.client.mutation({
|
|
355
|
+
__name: 'UpdateConfiguration',
|
|
356
|
+
updateConfiguration: {
|
|
357
|
+
__args: { periodId, key: definitionKey, data, validateOnly },
|
|
358
|
+
success: true,
|
|
359
|
+
messages: {
|
|
360
|
+
message: true,
|
|
361
|
+
type: true,
|
|
362
|
+
arg: true
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
})
|
|
366
|
+
return this.mutationForDialog(response.updateConfiguration)
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
async getAppRequestData (appRequestId: string) {
|
|
370
|
+
const response = await this.client.query({
|
|
371
|
+
__name: 'GetAppRequestData',
|
|
372
|
+
appRequests: {
|
|
373
|
+
__args: { filter: { ids: [appRequestId] } },
|
|
374
|
+
id: true,
|
|
375
|
+
data: true,
|
|
376
|
+
applications: {
|
|
377
|
+
requirements: {
|
|
378
|
+
prompts: {
|
|
379
|
+
key: true,
|
|
380
|
+
gatheredConfigData: true
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
})
|
|
386
|
+
if (response.appRequests.length === 0) return { data: {}, applications: [] as typeof response.appRequests[0]['applications'] }
|
|
387
|
+
const appRequest = response.appRequests[0]
|
|
388
|
+
return appRequest
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
static splitPromptsForApplicant<P extends { id: string, key: string, visibility: PromptVisibility, moot: boolean }, R extends { type: RequirementType, status: RequirementStatus, statusReason: string | null, blame?: string[] | null, prompts: P[] }, A extends { title: string, ineligiblePhase: IneligiblePhases | null, requirements: R[] }>(applications: A[]) {
|
|
392
|
+
type ReturnPrompt = P & { statusReasons: StatusReason[] }
|
|
393
|
+
type ReturnRequirement = R & { prompts: ReturnPrompt[] }
|
|
394
|
+
type ReturnApplication = A & { requirements: ReturnRequirement[], completionStatus: CompletionStatus, hasWarning: boolean, warningReasons: string[], ineligibleReasons: string[], metReasons: string[]}
|
|
395
|
+
const prequalPrompts: ReturnPrompt[] = []
|
|
396
|
+
const postqualPrompts: ReturnPrompt[] = []
|
|
397
|
+
const qualPrompts: ReturnPrompt[] = []
|
|
398
|
+
const applicationsReviewWithDupes: ReturnApplication[] = []
|
|
399
|
+
const applicationsReviewNoDupes: ReturnApplication[] = []
|
|
400
|
+
const applicationsForNavWithDupes: ReturnApplication[] = []
|
|
401
|
+
const applicationsForNavNoDupes: ReturnApplication[] = []
|
|
402
|
+
const applicationsAcceptWithDupes: ReturnApplication[] = []
|
|
403
|
+
const applicationsAcceptNoDupes: ReturnApplication[] = []
|
|
404
|
+
const promptsById: Record<string, ReturnPrompt> = {}
|
|
405
|
+
const promptsByKey: Record<string, ReturnPrompt[]> = {}
|
|
406
|
+
// every prompt object we hand out, dupes included, so the status reasons pass below reaches all of them
|
|
407
|
+
const allPromptRefs: ReturnPrompt[] = []
|
|
408
|
+
|
|
409
|
+
const seenForReview = new Set<string>()
|
|
410
|
+
const seenForNav = new Set<string>()
|
|
411
|
+
const seenForAccept = new Set<string>()
|
|
412
|
+
|
|
413
|
+
const requirementTypesForNavigation = new Set<RequirementType>([enumRequirementType.PREQUAL, enumRequirementType.POSTQUAL, enumRequirementType.QUALIFICATION])
|
|
414
|
+
const requirementTypesForReview = new Set<RequirementType>([enumRequirementType.QUALIFICATION, enumRequirementType.PREAPPROVAL, enumRequirementType.APPROVAL, enumRequirementType.WORKFLOW])
|
|
415
|
+
for (const application of applications) {
|
|
416
|
+
const requirementsReviewWithDupes: ReturnRequirement[] = []
|
|
417
|
+
const requirementsReviewNoDupes: ReturnRequirement[] = []
|
|
418
|
+
const requirementsForNavWithDupes: ReturnRequirement[] = []
|
|
419
|
+
const requirementsForNavNoDupes: ReturnRequirement[] = []
|
|
420
|
+
const requirementsAcceptWithDupes: ReturnRequirement[] = []
|
|
421
|
+
const requirementsAcceptNoDupes: ReturnRequirement[] = []
|
|
422
|
+
for (const requirement of application.requirements) {
|
|
423
|
+
const promptsForNavWithDupes: ReturnPrompt[] = []
|
|
424
|
+
const promptsForNavNoDupes: ReturnPrompt[] = []
|
|
425
|
+
const promptsReviewNoDupes: ReturnPrompt[] = []
|
|
426
|
+
const promptsReviewWithDupes: ReturnPrompt[] = []
|
|
427
|
+
const promptsAcceptWithDupes: ReturnPrompt[] = []
|
|
428
|
+
const promptsAcceptNoDupes: ReturnPrompt[] = []
|
|
429
|
+
for (const prompt of requirement.prompts) {
|
|
430
|
+
const retPrompt: ReturnPrompt = { ...prompt, statusReasons: [] }
|
|
431
|
+
const withDupesPrompt = { ...retPrompt }
|
|
432
|
+
allPromptRefs.push(retPrompt, withDupesPrompt)
|
|
433
|
+
promptsById[prompt.id] = retPrompt
|
|
434
|
+
if (prompt.moot || prompt.visibility === enumPromptVisibility.UNREACHABLE) continue
|
|
435
|
+
promptsByKey[prompt.key] ??= []
|
|
436
|
+
promptsByKey[prompt.key].push(retPrompt)
|
|
437
|
+
if (!applicantVisiblePromptVisibilities.has(prompt.visibility)) continue
|
|
438
|
+
promptsReviewWithDupes.push(withDupesPrompt)
|
|
439
|
+
if (requirementTypesForNavigation.has(requirement.type)) promptsForNavWithDupes.push(withDupesPrompt)
|
|
440
|
+
if (requirement.type === enumRequirementType.ACCEPTANCE) promptsAcceptWithDupes.push(withDupesPrompt)
|
|
441
|
+
if (!seenForReview.has(prompt.key)) promptsReviewNoDupes.push(retPrompt)
|
|
442
|
+
seenForReview.add(prompt.key)
|
|
443
|
+
if (requirementTypesForNavigation.has(requirement.type)) {
|
|
444
|
+
if (!seenForNav.has(prompt.key)) promptsForNavNoDupes.push(retPrompt)
|
|
445
|
+
seenForNav.add(prompt.key)
|
|
446
|
+
}
|
|
447
|
+
if (requirement.type === enumRequirementType.ACCEPTANCE) {
|
|
448
|
+
if (!seenForAccept.has(prompt.key)) promptsAcceptNoDupes.push(retPrompt)
|
|
449
|
+
seenForAccept.add(prompt.key)
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
if (requirement.type === enumRequirementType.PREQUAL) prequalPrompts.push(...promptsForNavNoDupes)
|
|
453
|
+
else if (requirement.type === enumRequirementType.POSTQUAL) postqualPrompts.push(...promptsForNavNoDupes)
|
|
454
|
+
else if (requirementTypesForReview.has(requirement.type)) {
|
|
455
|
+
requirementsReviewWithDupes.push({ ...requirement, prompts: promptsReviewWithDupes })
|
|
456
|
+
requirementsReviewNoDupes.push({ ...requirement, prompts: promptsReviewNoDupes })
|
|
457
|
+
if (requirement.type === enumRequirementType.QUALIFICATION) {
|
|
458
|
+
qualPrompts.push(...promptsForNavNoDupes)
|
|
459
|
+
requirementsForNavWithDupes.push({ ...requirement, prompts: promptsForNavWithDupes })
|
|
460
|
+
requirementsForNavNoDupes.push({ ...requirement, prompts: promptsForNavNoDupes })
|
|
461
|
+
}
|
|
462
|
+
} else if (requirement.type === enumRequirementType.ACCEPTANCE) {
|
|
463
|
+
requirementsAcceptWithDupes.push({ ...requirement, prompts: promptsAcceptWithDupes })
|
|
464
|
+
requirementsAcceptNoDupes.push({ ...requirement, prompts: promptsAcceptNoDupes })
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
const reqsForCompletion = application.requirements.filter(r => r.type !== enumRequirementType.POSTQUAL)
|
|
468
|
+
let completionStatus: CompletionStatus = (application.ineligiblePhase) ? 'INELIGIBLE' : 'ELIGIBLE' // ineligible is always precise state, while eligible could be in eligible or pending state
|
|
469
|
+
let completionStatusForNav: CompletionStatus = (application.ineligiblePhase) ? 'INELIGIBLE' : 'ELIGIBLE' // ineligible is always precise state, while eligible could be in eligible or pending state
|
|
470
|
+
const warningReasons: string[] = []
|
|
471
|
+
const warningReasonsFull: string[] = []
|
|
472
|
+
const ineligibleReasons: string[] = []
|
|
473
|
+
const ineligibleReasonsFull: string[] = []
|
|
474
|
+
const metReasons: string[] = []
|
|
475
|
+
const metReasonsFull: string[] = []
|
|
476
|
+
let hasWarning = false
|
|
477
|
+
let hasWarningForNav = false
|
|
478
|
+
for (const req of reqsForCompletion) {
|
|
479
|
+
const showWarnings = application.ineligiblePhase !== enumIneligiblePhases.PREQUAL || req.type === enumRequirementType.PREQUAL
|
|
480
|
+
// PENDING only demotes ELIGIBLE; INELIGIBLE is precise and PENDING just remains. The two statuses are
|
|
481
|
+
// demoted independently: the first PENDING requirement may be a non-navigation type (e.g. an APPROVAL
|
|
482
|
+
// requirement interleaved between QUALIFICATION ones), and that must not stop a later PENDING
|
|
483
|
+
// QUALIFICATION requirement from marking the nav status PENDING.
|
|
484
|
+
if (req.status === enumRequirementStatus.PENDING) {
|
|
485
|
+
if (completionStatus === 'ELIGIBLE') completionStatus = 'PENDING'
|
|
486
|
+
if (completionStatusForNav === 'ELIGIBLE' && requirementTypesForNavigation.has(req.type)) completionStatusForNav = 'PENDING'
|
|
487
|
+
}
|
|
488
|
+
//WARNING makes no completion status changes, whatever application state remains (pending, inel, eli), just append warning reasons
|
|
489
|
+
else if (req.status === enumRequirementStatus.WARNING) {
|
|
490
|
+
hasWarning = true
|
|
491
|
+
if (requirementTypesForNavigation.has(req.type)) hasWarningForNav = true
|
|
492
|
+
if (req.statusReason && showWarnings) {
|
|
493
|
+
if (requirementTypesForNavigation.has(req.type)) warningReasons.push(req.statusReason)
|
|
494
|
+
warningReasonsFull.push(req.statusReason)
|
|
495
|
+
}
|
|
496
|
+
// DISQUALIFYING, eligibility is already known by app ineligiblePhase, but always log ineligible reasons
|
|
497
|
+
// showWarnings above already limits PREQUAL-dead apps to their PREQUAL requirements' reasons
|
|
498
|
+
} else if (req.status === enumRequirementStatus.DISQUALIFYING) {
|
|
499
|
+
if (req.statusReason && showWarnings) {
|
|
500
|
+
if (requirementTypesForNavigation.has(req.type)) ineligibleReasons.push(req.statusReason)
|
|
501
|
+
ineligibleReasonsFull.push(req.statusReason)
|
|
502
|
+
}
|
|
503
|
+
// MET, eligibility is already known by app ineligiblePhase, but always log MET reasons
|
|
504
|
+
} else if (req.status === enumRequirementStatus.MET) {
|
|
505
|
+
if (req.statusReason) {
|
|
506
|
+
if (requirementTypesForNavigation.has(req.type)) metReasons.push(req.statusReason)
|
|
507
|
+
metReasonsFull.push(req.statusReason)
|
|
508
|
+
}
|
|
509
|
+
// NOT APPLICABLE, reference here is for completness, but not implemented as not_applicable currently changes nothing, nor logs reasons
|
|
510
|
+
} //else if (req.status === enumRequirementStatus.NOT_APPLICABLE) {
|
|
511
|
+
//}
|
|
512
|
+
}
|
|
513
|
+
applicationsReviewWithDupes.push({ ...application, requirements: requirementsReviewWithDupes, completionStatus, warningReasons: warningReasonsFull, ineligibleReasons: ineligibleReasonsFull, metReasons: metReasonsFull, hasWarning })
|
|
514
|
+
applicationsReviewNoDupes.push({ ...application, requirements: requirementsReviewNoDupes, completionStatus, warningReasons: warningReasonsFull, ineligibleReasons: ineligibleReasonsFull, metReasons: metReasonsFull, hasWarning })
|
|
515
|
+
applicationsForNavWithDupes.push({ ...application, requirements: requirementsForNavWithDupes, completionStatus: completionStatusForNav, warningReasons, ineligibleReasons, metReasons: metReasonsFull, hasWarning: hasWarningForNav })
|
|
516
|
+
applicationsForNavNoDupes.push({ ...application, requirements: requirementsForNavNoDupes, completionStatus: completionStatusForNav, warningReasons, ineligibleReasons, metReasons: metReasonsFull, hasWarning: hasWarningForNav })
|
|
517
|
+
applicationsAcceptWithDupes.push({ ...application, requirements: requirementsAcceptWithDupes, completionStatus, warningReasons: warningReasonsFull, ineligibleReasons: ineligibleReasonsFull, metReasons: metReasonsFull, hasWarning })
|
|
518
|
+
applicationsAcceptNoDupes.push({ ...application, requirements: requirementsAcceptNoDupes, completionStatus, warningReasons: warningReasonsFull, ineligibleReasons: ineligibleReasonsFull, metReasons: metReasonsFull, hasWarning })
|
|
519
|
+
}
|
|
520
|
+
const reasonsByKey: Record<string, StatusReason[]> = {}
|
|
521
|
+
for (const application of applications) {
|
|
522
|
+
for (const requirement of application.requirements) {
|
|
523
|
+
const blamed = !!requirement.blame?.length
|
|
524
|
+
const targets = blamed
|
|
525
|
+
? requirement.blame!
|
|
526
|
+
: requirement.prompts.filter(p => p.visibility !== enumPromptVisibility.UNREACHABLE).map(p => p.key)
|
|
527
|
+
for (const key of targets) {
|
|
528
|
+
(reasonsByKey[key] ??= []).push({ ...pick(requirement, 'status', 'statusReason'), programName: application.title, blamed })
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
for (const prompt of allPromptRefs) prompt.statusReasons = reasonsByKey[prompt.key] ?? []
|
|
533
|
+
return { prequalPrompts, postqualPrompts, qualPrompts, applicationsReviewWithDupes, applicationsReviewNoDupes, applicationsForNavWithDupes, applicationsForNavNoDupes, applicationsAcceptWithDupes, applicationsAcceptNoDupes, promptsByKey, promptsById }
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
async getAppRequestForExport (appRequestId: string) {
|
|
537
|
+
const response = await this.client.query({
|
|
538
|
+
__name: 'GetAppRequestForExport',
|
|
539
|
+
appRequests: {
|
|
540
|
+
__args: { filter: { ids: [appRequestId] } },
|
|
541
|
+
id: true,
|
|
542
|
+
status: true,
|
|
543
|
+
phase: true,
|
|
544
|
+
closedAt: true,
|
|
545
|
+
data: true,
|
|
546
|
+
dataVersion: true,
|
|
547
|
+
period: {
|
|
548
|
+
name: true
|
|
549
|
+
},
|
|
550
|
+
applications: {
|
|
551
|
+
id: true,
|
|
552
|
+
status: true,
|
|
553
|
+
rescindedStatus: true,
|
|
554
|
+
ineligiblePhase: true,
|
|
555
|
+
hiddenIneligiblePreSubmit: true,
|
|
556
|
+
statusReason: true,
|
|
557
|
+
title: true,
|
|
558
|
+
navTitle: true,
|
|
559
|
+
applicantDescription: true,
|
|
560
|
+
eligibilityDescription: true,
|
|
561
|
+
requirements: {
|
|
562
|
+
id: true,
|
|
563
|
+
type: true,
|
|
564
|
+
status: true,
|
|
565
|
+
statusReason: true,
|
|
566
|
+
blame: true,
|
|
567
|
+
prompts: {
|
|
568
|
+
id: true,
|
|
569
|
+
key: true,
|
|
570
|
+
title: true,
|
|
571
|
+
navTitle: true,
|
|
572
|
+
answered: true,
|
|
573
|
+
visibility: true,
|
|
574
|
+
moot: true,
|
|
575
|
+
invalidated: true,
|
|
576
|
+
invalidatedReason: true,
|
|
577
|
+
optOut: true
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
},
|
|
581
|
+
actions: {
|
|
582
|
+
viewApplyUI: true,
|
|
583
|
+
viewAcceptUI: true
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
})
|
|
587
|
+
if (response.appRequests.length === 0) throw error(404, 'Application request not found')
|
|
588
|
+
type ResponseAppRequest = (typeof response)['appRequests'][0]
|
|
589
|
+
type ResponseApplication = ResponseAppRequest['applications'][0] & { requirements: ResponseRequirement[] }
|
|
590
|
+
type ResponseRequirement = ResponseAppRequest['applications'][0]['requirements'][0] & { prompts: ResponsePrompt[] }
|
|
591
|
+
type ResponsePrompt = ResponseAppRequest['applications'][0]['requirements'][0]['prompts'][0] & PromptConfig
|
|
592
|
+
|
|
593
|
+
// configuration is per prompt key, not per prompt instance, so it is fetched once per key rather than
|
|
594
|
+
// repeated under every program and requirement that shares the prompt
|
|
595
|
+
const applications = (response.appRequests[0]?.applications ?? []) as ResponseApplication[]
|
|
596
|
+
await this.attachPromptConfigs(appRequestId, applications)
|
|
597
|
+
|
|
598
|
+
const splitInfo = API.splitPromptsForApplicant<ResponsePrompt, ResponseRequirement, ResponseApplication>(applications)
|
|
599
|
+
return {
|
|
600
|
+
...omit(splitInfo, 'applicationsForNavNoDupes', 'applicationsForNavWithDupes', 'applicationsReviewNoDupes', 'applicationsReviewWithDupes'),
|
|
601
|
+
applicationsForNav: showDupePrompts ? splitInfo.applicationsForNavWithDupes : splitInfo.applicationsForNavNoDupes,
|
|
602
|
+
applicationsReview: showDupePrompts ? splitInfo.applicationsReviewWithDupes : splitInfo.applicationsReviewNoDupes,
|
|
603
|
+
applicationsAccept: showDupePrompts ? splitInfo.applicationsAcceptWithDupes : splitInfo.applicationsAcceptNoDupes,
|
|
604
|
+
appRequest: response.appRequests[0]
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
async createAppRequest (periodId?: string, login?: string, validateOnly?: boolean) {
|
|
609
|
+
const messages: Feedback[] = []
|
|
610
|
+
if (!periodId) messages.push({ message: 'You must select a period to create an application.', type: 'error', path: 'periodId' })
|
|
611
|
+
if (!login) messages.push({ message: 'You must provide a login to create an application.', type: 'error', path: 'login' })
|
|
612
|
+
if (messages.length > 0) return { success: false, messages, data: { periodId, login }, id: undefined }
|
|
613
|
+
|
|
614
|
+
const response = await this.client.mutation({
|
|
615
|
+
__name: 'CreateAppRequest',
|
|
616
|
+
createAppRequest: {
|
|
617
|
+
__args: { periodId: periodId!, login: login!, validateOnly },
|
|
618
|
+
success: true,
|
|
619
|
+
messages: {
|
|
620
|
+
message: true,
|
|
621
|
+
type: true,
|
|
622
|
+
arg: true
|
|
623
|
+
},
|
|
624
|
+
appRequest: {
|
|
625
|
+
id: true
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
})
|
|
629
|
+
return { ...this.mutationForDialog(response.createAppRequest), id: response.createAppRequest.appRequest?.id }
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
async appRequestPhaseChange (appRequestId: string, phaseChange: PhaseChangeMutations) {
|
|
633
|
+
const response = await this.graphql<Record<string, { success: boolean, messages: { message: string, type: 'error' | 'warning' | 'success', arg?: string }[] }>>(`
|
|
634
|
+
mutation AppRequestPhaseChange($appRequestId: ID!) {
|
|
635
|
+
${phaseChange}(appRequestId: $appRequestId) {
|
|
636
|
+
success
|
|
637
|
+
messages {
|
|
638
|
+
message
|
|
639
|
+
type
|
|
640
|
+
arg
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
`, { appRequestId })
|
|
645
|
+
return this.mutationForDialog(response[phaseChange])
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
async cancelAppRequest (appRequestId: string, dataVersion?: number) {
|
|
649
|
+
const response = await this.client.mutation({
|
|
650
|
+
__name: 'CancelAppRequest',
|
|
651
|
+
cancelAppRequest: {
|
|
652
|
+
__args: { appRequestId, dataVersion },
|
|
653
|
+
success: true,
|
|
654
|
+
messages: {
|
|
655
|
+
message: true,
|
|
656
|
+
type: true,
|
|
657
|
+
arg: true
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
})
|
|
661
|
+
return this.mutationForDialog(response.cancelAppRequest)
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
async reopenAppRequest (appRequestId: string) {
|
|
665
|
+
const response = await this.client.mutation({
|
|
666
|
+
__name: 'ReopenAppRequest',
|
|
667
|
+
reopenAppRequest: {
|
|
668
|
+
__args: { appRequestId },
|
|
669
|
+
success: true,
|
|
670
|
+
messages: {
|
|
671
|
+
message: true,
|
|
672
|
+
type: true,
|
|
673
|
+
arg: true
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
})
|
|
677
|
+
return this.mutationForDialog(response.reopenAppRequest)
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
async advanceWorkflow (applicationId: string) {
|
|
681
|
+
const response = await this.client.mutation({
|
|
682
|
+
__name: 'AdvanceWorkflow',
|
|
683
|
+
advanceWorkflow: {
|
|
684
|
+
__args: { applicationId },
|
|
685
|
+
success: true,
|
|
686
|
+
messages: {
|
|
687
|
+
message: true,
|
|
688
|
+
type: true,
|
|
689
|
+
arg: true
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
})
|
|
693
|
+
return this.mutationForDialog(response.advanceWorkflow)
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
async reverseWorkflow (applicationId: string) {
|
|
697
|
+
const response = await this.client.mutation({
|
|
698
|
+
__name: 'ReverseWorkflow',
|
|
699
|
+
reverseWorkflow: {
|
|
700
|
+
__args: { applicationId },
|
|
701
|
+
success: true,
|
|
702
|
+
messages: {
|
|
703
|
+
message: true,
|
|
704
|
+
type: true,
|
|
705
|
+
arg: true
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
})
|
|
709
|
+
return this.mutationForDialog(response.reverseWorkflow)
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
async rescind (applicationId: string, reason: string, validateOnly?: boolean) {
|
|
713
|
+
const response = await this.client.mutation({
|
|
714
|
+
__name: 'Rescind',
|
|
715
|
+
rescind: {
|
|
716
|
+
__args: { applicationId, reason, validateOnly },
|
|
717
|
+
success: true,
|
|
718
|
+
messages: {
|
|
719
|
+
message: true,
|
|
720
|
+
type: true,
|
|
721
|
+
arg: true
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
})
|
|
725
|
+
return this.mutationForDialog(response.rescind)
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
async restore (applicationId: string, reason: string, validateOnly?: boolean) {
|
|
729
|
+
const response = await this.client.mutation({
|
|
730
|
+
__name: 'Restore',
|
|
731
|
+
restore: {
|
|
732
|
+
__args: { applicationId, reason, validateOnly },
|
|
733
|
+
success: true,
|
|
734
|
+
messages: {
|
|
735
|
+
message: true,
|
|
736
|
+
type: true,
|
|
737
|
+
arg: true
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
})
|
|
741
|
+
return this.mutationForDialog(response.restore)
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
async addNote (appRequestId: string, content: string, persistent?: boolean, validateOnly?: boolean) {
|
|
745
|
+
const response = await this.client.mutation({
|
|
746
|
+
__name: 'AddNote',
|
|
747
|
+
addNote: {
|
|
748
|
+
__args: { appRequestId, content, persistent, validateOnly },
|
|
749
|
+
success: true,
|
|
750
|
+
messages: {
|
|
751
|
+
message: true,
|
|
752
|
+
type: true,
|
|
753
|
+
arg: true
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
})
|
|
757
|
+
return this.mutationForDialog(response.addNote, { dataName: 'note' })
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
async updateNote (noteId: string, content: string) {
|
|
761
|
+
const response = await this.client.mutation({
|
|
762
|
+
__name: 'UpdateNote',
|
|
763
|
+
updateNote: {
|
|
764
|
+
__args: { noteId, content },
|
|
765
|
+
success: true,
|
|
766
|
+
messages: {
|
|
767
|
+
message: true,
|
|
768
|
+
type: true,
|
|
769
|
+
arg: true
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
})
|
|
773
|
+
return this.mutationForDialog(response.updateNote, { dataName: 'note' })
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
async togglePersistence (noteId: string) {
|
|
777
|
+
const response = await this.client.mutation({
|
|
778
|
+
__name: 'TogglePersistence',
|
|
779
|
+
togglePersistence: {
|
|
780
|
+
__args: { noteId },
|
|
781
|
+
success: true,
|
|
782
|
+
messages: {
|
|
783
|
+
message: true,
|
|
784
|
+
type: true,
|
|
785
|
+
arg: true
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
})
|
|
789
|
+
return this.mutationForDialog(response.togglePersistence, { dataName: 'note' })
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
async deleteNote (noteId: string) {
|
|
793
|
+
const response = await this.client.mutation({
|
|
794
|
+
__name: 'DeleteNote',
|
|
795
|
+
deleteNote: {
|
|
796
|
+
__args: { noteId }
|
|
797
|
+
}
|
|
798
|
+
})
|
|
799
|
+
return response.deleteNote
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
async closeAppRequest (appRequestId: string) {
|
|
803
|
+
const response = await this.client.mutation({
|
|
804
|
+
__name: 'CloseAppRequest',
|
|
805
|
+
closeAppRequest: {
|
|
806
|
+
__args: { appRequestId },
|
|
807
|
+
success: true,
|
|
808
|
+
messages: {
|
|
809
|
+
message: true,
|
|
810
|
+
type: true,
|
|
811
|
+
arg: true
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
})
|
|
815
|
+
return this.mutationForDialog(response.closeAppRequest)
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
async getAppRequests (
|
|
819
|
+
filter?: Omit<AppRequestFilter, 'indexes'> & { indexes?: Record<string, string[]> },
|
|
820
|
+
paged?: Pagination,
|
|
821
|
+
dest = enumAppRequestIndexDestination.APP_REQUEST_LIST) {
|
|
822
|
+
const processedFilter = {
|
|
823
|
+
...filter,
|
|
824
|
+
indexes: filter?.indexes ? Object.entries(filter.indexes).map(([category, tags]) => ({ category, tags })) : undefined
|
|
825
|
+
}
|
|
826
|
+
const response = await this.client.query({
|
|
827
|
+
__name: 'GetAppRequests',
|
|
828
|
+
appRequests: {
|
|
829
|
+
__args: { filter: processedFilter, paged },
|
|
830
|
+
id: true,
|
|
831
|
+
phase: true,
|
|
832
|
+
createdAt: true,
|
|
833
|
+
closedAt: true,
|
|
834
|
+
updatedAt: true,
|
|
835
|
+
applications: {
|
|
836
|
+
title: true,
|
|
837
|
+
programKey: true,
|
|
838
|
+
status: true,
|
|
839
|
+
rescindedStatus: true
|
|
840
|
+
},
|
|
841
|
+
applicant: {
|
|
842
|
+
login: true,
|
|
843
|
+
fullname: true
|
|
844
|
+
},
|
|
845
|
+
status: true,
|
|
846
|
+
statusReason: true,
|
|
847
|
+
period: {
|
|
848
|
+
id: true,
|
|
849
|
+
name: true
|
|
850
|
+
},
|
|
851
|
+
indexCategories: {
|
|
852
|
+
__args: { for: dest },
|
|
853
|
+
category: true,
|
|
854
|
+
categoryLabel: true,
|
|
855
|
+
values: {
|
|
856
|
+
value: true,
|
|
857
|
+
label: true
|
|
858
|
+
}
|
|
859
|
+
},
|
|
860
|
+
actions: {
|
|
861
|
+
review: true
|
|
862
|
+
}
|
|
863
|
+
},
|
|
864
|
+
appRequestIndexes: {
|
|
865
|
+
category: true,
|
|
866
|
+
categoryLabel: true,
|
|
867
|
+
appRequestListPriority: true,
|
|
868
|
+
listFiltersPriority: true,
|
|
869
|
+
listable: true,
|
|
870
|
+
values: {
|
|
871
|
+
__args: { inUse: true },
|
|
872
|
+
value: true,
|
|
873
|
+
label: true
|
|
874
|
+
}
|
|
875
|
+
},
|
|
876
|
+
pageInfo: {
|
|
877
|
+
appRequests: {
|
|
878
|
+
currentPage: true,
|
|
879
|
+
totalItems: true,
|
|
880
|
+
hasNextPage: true,
|
|
881
|
+
perPage: true,
|
|
882
|
+
categories: {
|
|
883
|
+
tags: {
|
|
884
|
+
tag: true,
|
|
885
|
+
label: true
|
|
886
|
+
},
|
|
887
|
+
category: true,
|
|
888
|
+
label: true,
|
|
889
|
+
useInFilters: true,
|
|
890
|
+
useInList: true
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
})
|
|
896
|
+
return response
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
async searchIndexItems (category: string, search: string) {
|
|
900
|
+
const response = await this.client.query({
|
|
901
|
+
__name: 'SearchIndexItems',
|
|
902
|
+
appRequestIndexes: {
|
|
903
|
+
__args: { categories: [category] },
|
|
904
|
+
values: {
|
|
905
|
+
__args: { search },
|
|
906
|
+
value: true,
|
|
907
|
+
label: true
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
})
|
|
911
|
+
return response.appRequestIndexes[0]?.values ?? []
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
async getBasicRequestData (appRequestId: string) {
|
|
915
|
+
const response = await this.client.query({
|
|
916
|
+
__name: 'GetBasicRequestData',
|
|
917
|
+
appRequests: {
|
|
918
|
+
__args: { filter: { ids: [appRequestId] } },
|
|
919
|
+
status: true,
|
|
920
|
+
phase: true,
|
|
921
|
+
closedAt: true,
|
|
922
|
+
applicant: {
|
|
923
|
+
login: true,
|
|
924
|
+
fullname: true,
|
|
925
|
+
otherIdentifiers: {
|
|
926
|
+
id: true,
|
|
927
|
+
label: true
|
|
928
|
+
},
|
|
929
|
+
otherInfo: true
|
|
930
|
+
},
|
|
931
|
+
period: {
|
|
932
|
+
id: true,
|
|
933
|
+
name: true,
|
|
934
|
+
code: true,
|
|
935
|
+
openDate: true,
|
|
936
|
+
closeDate: true,
|
|
937
|
+
archiveDate: true
|
|
938
|
+
},
|
|
939
|
+
applications: {
|
|
940
|
+
id: true,
|
|
941
|
+
navTitle: true,
|
|
942
|
+
programKey: true,
|
|
943
|
+
status: true,
|
|
944
|
+
ineligiblePhase: true
|
|
945
|
+
},
|
|
946
|
+
actions: {
|
|
947
|
+
acceptOffer: true,
|
|
948
|
+
cancel: true,
|
|
949
|
+
close: true,
|
|
950
|
+
completeRequest: true,
|
|
951
|
+
completeReview: true,
|
|
952
|
+
reopen: true,
|
|
953
|
+
returnToApplicant: true,
|
|
954
|
+
returnToNonBlocking: true,
|
|
955
|
+
returnToOffer: true,
|
|
956
|
+
returnToReview: true,
|
|
957
|
+
review: true,
|
|
958
|
+
submit: true
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
})
|
|
962
|
+
if (response.appRequests.length === 0) return undefined
|
|
963
|
+
return response.appRequests[0]
|
|
964
|
+
}
|
|
965
|
+
|
|
966
|
+
async getReviewData (appRequestId: string, programKey?: string) {
|
|
967
|
+
const response = await this.client.query({
|
|
968
|
+
__name: 'GetReviewData',
|
|
969
|
+
appRequests: {
|
|
970
|
+
__args: { filter: { ids: [appRequestId] } },
|
|
971
|
+
id: true,
|
|
972
|
+
dataVersion: true,
|
|
973
|
+
status: true,
|
|
974
|
+
phase: true,
|
|
975
|
+
createdAt: true,
|
|
976
|
+
submittedAt: true,
|
|
977
|
+
closedAt: true,
|
|
978
|
+
data: true,
|
|
979
|
+
applications: {
|
|
980
|
+
...(programKey ? { __args: { programKeys: [programKey] } } : {}),
|
|
981
|
+
id: true,
|
|
982
|
+
phase: true,
|
|
983
|
+
status: true,
|
|
984
|
+
statusReason: true,
|
|
985
|
+
title: true,
|
|
986
|
+
navTitle: true,
|
|
987
|
+
programKey: true,
|
|
988
|
+
rescindedStatus: true,
|
|
989
|
+
workflowStage: {
|
|
990
|
+
key: true,
|
|
991
|
+
blocking: true
|
|
992
|
+
},
|
|
993
|
+
nextWorkflowStage: {
|
|
994
|
+
key: true,
|
|
995
|
+
title: true
|
|
996
|
+
},
|
|
997
|
+
previousWorkflowStage: {
|
|
998
|
+
key: true,
|
|
999
|
+
title: true
|
|
1000
|
+
},
|
|
1001
|
+
actions: {
|
|
1002
|
+
advanceWorkflow: true,
|
|
1003
|
+
reverseWorkflow: true,
|
|
1004
|
+
rescindApplication: true,
|
|
1005
|
+
restoreApplication: true
|
|
1006
|
+
},
|
|
1007
|
+
requirements: {
|
|
1008
|
+
id: true,
|
|
1009
|
+
key: true,
|
|
1010
|
+
type: true,
|
|
1011
|
+
title: true,
|
|
1012
|
+
status: true,
|
|
1013
|
+
statusReason: true,
|
|
1014
|
+
blame: true,
|
|
1015
|
+
workflowStage: {
|
|
1016
|
+
key: true,
|
|
1017
|
+
title: true,
|
|
1018
|
+
blocking: true
|
|
1019
|
+
},
|
|
1020
|
+
prompts: {
|
|
1021
|
+
id: true,
|
|
1022
|
+
key: true,
|
|
1023
|
+
title: true,
|
|
1024
|
+
navTitle: true,
|
|
1025
|
+
answered: true,
|
|
1026
|
+
visibility: true,
|
|
1027
|
+
moot: true,
|
|
1028
|
+
invalidated: true,
|
|
1029
|
+
invalidatedReason: true,
|
|
1030
|
+
optOut: true,
|
|
1031
|
+
noDisplay: true,
|
|
1032
|
+
actions: {
|
|
1033
|
+
update: true
|
|
1034
|
+
}
|
|
1035
|
+
}
|
|
1036
|
+
}
|
|
1037
|
+
},
|
|
1038
|
+
notes: {
|
|
1039
|
+
id: true,
|
|
1040
|
+
content: true,
|
|
1041
|
+
persistent: true,
|
|
1042
|
+
createdAt: true,
|
|
1043
|
+
author: {
|
|
1044
|
+
login: true,
|
|
1045
|
+
fullname: true
|
|
1046
|
+
}
|
|
1047
|
+
},
|
|
1048
|
+
actions: {
|
|
1049
|
+
acceptOffer: true,
|
|
1050
|
+
cancel: true,
|
|
1051
|
+
close: true,
|
|
1052
|
+
completeRequest: true,
|
|
1053
|
+
completeReview: true,
|
|
1054
|
+
reopen: true,
|
|
1055
|
+
returnToApplicant: true,
|
|
1056
|
+
returnToNonBlocking: true,
|
|
1057
|
+
returnToOffer: true,
|
|
1058
|
+
returnToReview: true,
|
|
1059
|
+
review: true,
|
|
1060
|
+
submit: true,
|
|
1061
|
+
createNote: true,
|
|
1062
|
+
createPersistentNote: true
|
|
1063
|
+
}
|
|
1064
|
+
}
|
|
1065
|
+
})
|
|
1066
|
+
if (response.appRequests.length === 0) return undefined
|
|
1067
|
+
const appRequest = response.appRequests[0]
|
|
1068
|
+
type ResponsePrompt = (typeof appRequest)['applications'][0]['requirements'][0]['prompts'][0] & PromptConfig
|
|
1069
|
+
type ResponseRequirement = Omit<(typeof appRequest)['applications'][0]['requirements'][0], 'prompts'> & { prompts: ResponsePrompt[] }
|
|
1070
|
+
type ResponseApplication = Omit<(typeof appRequest)['applications'][0], 'requirements'> & { requirements: ResponseRequirement[] }
|
|
1071
|
+
const applications = appRequest.applications as ResponseApplication[]
|
|
1072
|
+
await this.attachPromptConfigs(appRequestId, applications)
|
|
1073
|
+
return { ...appRequest, applications }
|
|
1074
|
+
}
|
|
1075
|
+
|
|
1076
|
+
async getRequestActivity (appRequestId: string, filters?: AppRequestActivityFilters, paged?: Pagination) {
|
|
1077
|
+
const response = await this.client.query({
|
|
1078
|
+
__name: 'GetRequestActivity',
|
|
1079
|
+
appRequestActivity: {
|
|
1080
|
+
__args: { id: appRequestId, filters, paged },
|
|
1081
|
+
id: true,
|
|
1082
|
+
user: {
|
|
1083
|
+
login: true,
|
|
1084
|
+
fullname: true
|
|
1085
|
+
},
|
|
1086
|
+
impersonatedBy: {
|
|
1087
|
+
login: true,
|
|
1088
|
+
fullname: true
|
|
1089
|
+
},
|
|
1090
|
+
action: true,
|
|
1091
|
+
description: true,
|
|
1092
|
+
data: true,
|
|
1093
|
+
createdAt: true
|
|
1094
|
+
},
|
|
1095
|
+
appRequestActivityActions: {
|
|
1096
|
+
__args: { id: appRequestId }
|
|
1097
|
+
},
|
|
1098
|
+
// Includes admins who only appear as impersonators
|
|
1099
|
+
appRequestActivityUsers: {
|
|
1100
|
+
__args: { id: appRequestId },
|
|
1101
|
+
login: true,
|
|
1102
|
+
fullname: true
|
|
1103
|
+
},
|
|
1104
|
+
pageInfo: {
|
|
1105
|
+
appRequestsActivity: {
|
|
1106
|
+
currentPage: true,
|
|
1107
|
+
totalItems: true,
|
|
1108
|
+
hasNextPage: true,
|
|
1109
|
+
perPage: true
|
|
1110
|
+
}
|
|
1111
|
+
}
|
|
1112
|
+
})
|
|
1113
|
+
return {
|
|
1114
|
+
activity: response.appRequestActivity,
|
|
1115
|
+
actions: response.appRequestActivityActions,
|
|
1116
|
+
people: response.appRequestActivityUsers,
|
|
1117
|
+
pageInfo: response.pageInfo.appRequestsActivity
|
|
1118
|
+
}
|
|
1119
|
+
}
|
|
1120
|
+
|
|
1121
|
+
async getPromptData (appRequestId: string, promptId: string) {
|
|
1122
|
+
const response = await this.client.query({
|
|
1123
|
+
__name: 'GetPromptData',
|
|
1124
|
+
appRequests: {
|
|
1125
|
+
__args: { filter: { ids: [appRequestId] } },
|
|
1126
|
+
dataVersion: true,
|
|
1127
|
+
prompt: {
|
|
1128
|
+
__args: { promptId },
|
|
1129
|
+
data: true,
|
|
1130
|
+
preloadData: true,
|
|
1131
|
+
prestageData: true,
|
|
1132
|
+
configurationData: true,
|
|
1133
|
+
gatheredConfigData: true,
|
|
1134
|
+
fetchedData: true
|
|
1135
|
+
}
|
|
1136
|
+
}
|
|
1137
|
+
})
|
|
1138
|
+
const appRequest = response.appRequests[0]
|
|
1139
|
+
return { ...appRequest.prompt, dataVersion: appRequest.dataVersion }
|
|
1140
|
+
}
|
|
1141
|
+
|
|
1142
|
+
/**
|
|
1143
|
+
* What the reviewer sidebar's "Ineligibility Information" dialog shows: every application's outcome plus
|
|
1144
|
+
* the statuses behind it. Loaded when the dialog opens, since the review page itself only carries the
|
|
1145
|
+
* displayed program.
|
|
1146
|
+
*/
|
|
1147
|
+
async getIneligibilityInfo (appRequestId: string) {
|
|
1148
|
+
const response = await this.client.query({
|
|
1149
|
+
__name: 'GetIneligibilityInfo',
|
|
1150
|
+
appRequests: {
|
|
1151
|
+
__args: { filter: { ids: [appRequestId] } },
|
|
1152
|
+
id: true,
|
|
1153
|
+
phase: true,
|
|
1154
|
+
closedAt: true,
|
|
1155
|
+
applications: {
|
|
1156
|
+
id: true,
|
|
1157
|
+
title: true,
|
|
1158
|
+
status: true,
|
|
1159
|
+
statusReason: true,
|
|
1160
|
+
rescindedStatus: true,
|
|
1161
|
+
requirements: {
|
|
1162
|
+
id: true,
|
|
1163
|
+
status: true,
|
|
1164
|
+
statusReason: true,
|
|
1165
|
+
prompts: {
|
|
1166
|
+
optOut: true
|
|
1167
|
+
}
|
|
1168
|
+
}
|
|
1169
|
+
}
|
|
1170
|
+
}
|
|
1171
|
+
})
|
|
1172
|
+
return response.appRequests[0]
|
|
1173
|
+
}
|
|
1174
|
+
|
|
1175
|
+
async getPromptConfigs (appRequestId: string, promptIds: string[]) {
|
|
1176
|
+
if (!promptIds.length) return []
|
|
1177
|
+
const response = await this.client.query({
|
|
1178
|
+
__name: 'GetPromptConfigs',
|
|
1179
|
+
appRequests: {
|
|
1180
|
+
__args: { filter: { ids: [appRequestId] } },
|
|
1181
|
+
applications: {
|
|
1182
|
+
requirements: {
|
|
1183
|
+
prompts: {
|
|
1184
|
+
__args: { filter: { ids: promptIds } },
|
|
1185
|
+
id: true,
|
|
1186
|
+
key: true,
|
|
1187
|
+
configurationData: true,
|
|
1188
|
+
gatheredConfigData: true
|
|
1189
|
+
}
|
|
1190
|
+
}
|
|
1191
|
+
}
|
|
1192
|
+
}
|
|
1193
|
+
})
|
|
1194
|
+
return response.appRequests[0]?.applications.flatMap(a => a.requirements.flatMap(r => r.prompts)) ?? []
|
|
1195
|
+
}
|
|
1196
|
+
|
|
1197
|
+
private async attachPromptConfigs<P extends { id: string, key: string, visibility: PromptVisibility, moot: boolean | null }> (appRequestId: string, applications: { requirements: { prompts: P[] }[] }[]) {
|
|
1198
|
+
const representativeByKey: Record<string, string> = {}
|
|
1199
|
+
const instancesByKey: Record<string, P[]> = {}
|
|
1200
|
+
for (const application of applications) {
|
|
1201
|
+
for (const requirement of application.requirements) {
|
|
1202
|
+
for (const prompt of requirement.prompts) {
|
|
1203
|
+
(instancesByKey[prompt.key] ??= []).push(prompt)
|
|
1204
|
+
if (representativeByKey[prompt.key] == null && !prompt.moot && prompt.visibility !== enumPromptVisibility.UNREACHABLE) representativeByKey[prompt.key] = prompt.id
|
|
1205
|
+
}
|
|
1206
|
+
}
|
|
1207
|
+
}
|
|
1208
|
+
const configs = await this.getPromptConfigs(appRequestId, Object.values(representativeByKey))
|
|
1209
|
+
const configByKey = keyby(configs, 'key')
|
|
1210
|
+
for (const [key, instances] of Object.entries(instancesByKey)) {
|
|
1211
|
+
const config = configByKey[key]
|
|
1212
|
+
for (const prompt of instances) {
|
|
1213
|
+
(prompt as P & PromptConfig).configurationData = config?.configurationData ?? {}
|
|
1214
|
+
;(prompt as P & PromptConfig).gatheredConfigData = config?.gatheredConfigData ?? {}
|
|
1215
|
+
}
|
|
1216
|
+
}
|
|
1217
|
+
}
|
|
1218
|
+
|
|
1219
|
+
async getPromptDataLegion (appRequestId: string, promptIds: string[]) {
|
|
1220
|
+
const response = await this.client.query({
|
|
1221
|
+
__name: 'GetPromptDataLegion',
|
|
1222
|
+
appRequests: {
|
|
1223
|
+
__args: { filter: { ids: [appRequestId] } },
|
|
1224
|
+
applications: {
|
|
1225
|
+
requirements: {
|
|
1226
|
+
prompts: {
|
|
1227
|
+
__args: { filter: { ids: promptIds } },
|
|
1228
|
+
id: true,
|
|
1229
|
+
data: true,
|
|
1230
|
+
preloadData: true,
|
|
1231
|
+
prestageData: true,
|
|
1232
|
+
configurationData: true,
|
|
1233
|
+
gatheredConfigData: true,
|
|
1234
|
+
fetchedData: true
|
|
1235
|
+
}
|
|
1236
|
+
}
|
|
1237
|
+
}
|
|
1238
|
+
}
|
|
1239
|
+
})
|
|
1240
|
+
const appRequest = response.appRequests[0]
|
|
1241
|
+
return appRequest.applications.flatMap(req => req.requirements.flatMap(r => r.prompts))
|
|
1242
|
+
}
|
|
1243
|
+
|
|
1244
|
+
async getPeriodList (filter?: PeriodFilters) {
|
|
1245
|
+
const response = await this.client.query({
|
|
1246
|
+
__name: 'GetPeriodList',
|
|
1247
|
+
periods: {
|
|
1248
|
+
__args: { filter },
|
|
1249
|
+
id: true,
|
|
1250
|
+
name: true,
|
|
1251
|
+
code: true,
|
|
1252
|
+
openDate: true,
|
|
1253
|
+
closeDate: true,
|
|
1254
|
+
archiveDate: true,
|
|
1255
|
+
reviewed: true,
|
|
1256
|
+
actions: {
|
|
1257
|
+
update: true,
|
|
1258
|
+
delete: true,
|
|
1259
|
+
createAppRequest: true
|
|
1260
|
+
}
|
|
1261
|
+
}
|
|
1262
|
+
})
|
|
1263
|
+
return response.periods
|
|
1264
|
+
}
|
|
1265
|
+
|
|
1266
|
+
async getOpenPeriods (filter?: PeriodFilters) {
|
|
1267
|
+
const response = await this.client.query({
|
|
1268
|
+
__name: 'GetOpenPeriods',
|
|
1269
|
+
periods: {
|
|
1270
|
+
__args: { filter: { ...filter, openNow: true } },
|
|
1271
|
+
id: true,
|
|
1272
|
+
name: true,
|
|
1273
|
+
code: true,
|
|
1274
|
+
openDate: true,
|
|
1275
|
+
closeDate: true,
|
|
1276
|
+
archiveDate: true,
|
|
1277
|
+
reviewed: true
|
|
1278
|
+
}
|
|
1279
|
+
})
|
|
1280
|
+
return response.periods
|
|
1281
|
+
}
|
|
1282
|
+
|
|
1283
|
+
async createPeriod (period: PeriodUpdate, validateOnly: boolean, copyPeriodId?: string) {
|
|
1284
|
+
const response = await this.client.mutation({
|
|
1285
|
+
__name: 'CreatePeriod',
|
|
1286
|
+
createPeriod: {
|
|
1287
|
+
__args: { period, validateOnly, copyPeriodId },
|
|
1288
|
+
success: true,
|
|
1289
|
+
messages: {
|
|
1290
|
+
message: true,
|
|
1291
|
+
type: true,
|
|
1292
|
+
arg: true
|
|
1293
|
+
}
|
|
1294
|
+
}
|
|
1295
|
+
})
|
|
1296
|
+
return api.mutationForDialog(response.createPeriod, { prefix: 'period' })
|
|
1297
|
+
}
|
|
1298
|
+
|
|
1299
|
+
async updatePeriod (periodId: string, period: PeriodUpdate, validateOnly: boolean) {
|
|
1300
|
+
const response = await this.client.mutation({
|
|
1301
|
+
__name: 'UpdatePeriod',
|
|
1302
|
+
updatePeriod: {
|
|
1303
|
+
__args: { periodId, update: period, validateOnly },
|
|
1304
|
+
success: true,
|
|
1305
|
+
messages: {
|
|
1306
|
+
message: true,
|
|
1307
|
+
type: true,
|
|
1308
|
+
arg: true
|
|
1309
|
+
}
|
|
1310
|
+
}
|
|
1311
|
+
})
|
|
1312
|
+
return this.mutationForDialog(response.updatePeriod, { prefix: 'period' })
|
|
1313
|
+
}
|
|
1314
|
+
|
|
1315
|
+
async markPeriodReviewed (periodId: string, validateOnly: boolean) {
|
|
1316
|
+
const response = await this.client.mutation({
|
|
1317
|
+
__name: 'MarkPeriodReviewed',
|
|
1318
|
+
markPeriodReviewed: {
|
|
1319
|
+
__args: { periodId, validateOnly },
|
|
1320
|
+
success: true,
|
|
1321
|
+
messages: {
|
|
1322
|
+
message: true,
|
|
1323
|
+
type: true,
|
|
1324
|
+
arg: true
|
|
1325
|
+
}
|
|
1326
|
+
}
|
|
1327
|
+
})
|
|
1328
|
+
return this.mutationForDialog(response.markPeriodReviewed, { prefix: 'period' })
|
|
1329
|
+
}
|
|
1330
|
+
|
|
1331
|
+
async deletePeriod (periodId: string) {
|
|
1332
|
+
const response = await this.client.mutation({
|
|
1333
|
+
__name: 'DeletePeriod',
|
|
1334
|
+
deletePeriod: {
|
|
1335
|
+
__args: { periodId },
|
|
1336
|
+
success: true
|
|
1337
|
+
}
|
|
1338
|
+
})
|
|
1339
|
+
return response.deletePeriod.success
|
|
1340
|
+
}
|
|
1341
|
+
|
|
1342
|
+
async disablePeriodProgramRequirements (periodId: string, requirementKey: string, disabled: boolean) {
|
|
1343
|
+
const response = await this.client.mutation({
|
|
1344
|
+
__name: 'updatePeriodProgram',
|
|
1345
|
+
updatePeriodRequirement: {
|
|
1346
|
+
__args: { periodId, requirementKey, disabled },
|
|
1347
|
+
success: true
|
|
1348
|
+
}
|
|
1349
|
+
})
|
|
1350
|
+
return response.updatePeriodRequirement.success
|
|
1351
|
+
}
|
|
1352
|
+
|
|
1353
|
+
async getPeriodConfigurations (periodId: string) {
|
|
1354
|
+
const response = await this.client.query({
|
|
1355
|
+
__name: 'GetPeriodConfigurations',
|
|
1356
|
+
periods: {
|
|
1357
|
+
__args: { filter: { ids: [periodId] } },
|
|
1358
|
+
id: true,
|
|
1359
|
+
name: true,
|
|
1360
|
+
code: true,
|
|
1361
|
+
openDate: true,
|
|
1362
|
+
closeDate: true,
|
|
1363
|
+
archiveDate: true,
|
|
1364
|
+
reviewed: true,
|
|
1365
|
+
programs: {
|
|
1366
|
+
key: true,
|
|
1367
|
+
title: true,
|
|
1368
|
+
navTitle: true,
|
|
1369
|
+
enabled: true,
|
|
1370
|
+
configuration: {
|
|
1371
|
+
data: true,
|
|
1372
|
+
actions: {
|
|
1373
|
+
update: true
|
|
1374
|
+
}
|
|
1375
|
+
},
|
|
1376
|
+
requirements: {
|
|
1377
|
+
key: true,
|
|
1378
|
+
title: true,
|
|
1379
|
+
description: true,
|
|
1380
|
+
enabled: true,
|
|
1381
|
+
type: true,
|
|
1382
|
+
configuration: {
|
|
1383
|
+
data: true,
|
|
1384
|
+
actions: {
|
|
1385
|
+
update: true
|
|
1386
|
+
}
|
|
1387
|
+
},
|
|
1388
|
+
prompts: {
|
|
1389
|
+
key: true,
|
|
1390
|
+
title: true,
|
|
1391
|
+
description: true,
|
|
1392
|
+
configuration: {
|
|
1393
|
+
data: true,
|
|
1394
|
+
actions: {
|
|
1395
|
+
update: true
|
|
1396
|
+
}
|
|
1397
|
+
}
|
|
1398
|
+
}
|
|
1399
|
+
}
|
|
1400
|
+
}
|
|
1401
|
+
}
|
|
1402
|
+
})
|
|
1403
|
+
if (!response.periods.length) return { period: undefined, programs: [] }
|
|
1404
|
+
const period = response.periods[0]
|
|
1405
|
+
return { programs: period.programs, period }
|
|
1406
|
+
}
|
|
1407
|
+
|
|
1408
|
+
async getConfigurationFetched (periodId: string, definitionKey: string) {
|
|
1409
|
+
const response = await this.client.query({
|
|
1410
|
+
__name: 'GetConfigurationFetched',
|
|
1411
|
+
periods: {
|
|
1412
|
+
__args: { filter: { ids: [periodId] } },
|
|
1413
|
+
configurations: {
|
|
1414
|
+
__args: { filter: { keys: [definitionKey] } },
|
|
1415
|
+
fetchedData: true
|
|
1416
|
+
}
|
|
1417
|
+
}
|
|
1418
|
+
})
|
|
1419
|
+
return response.periods[0].configurations[0]?.fetchedData ?? {}
|
|
1420
|
+
}
|
|
1421
|
+
|
|
1422
|
+
async getRoleList () {
|
|
1423
|
+
const response = await this.client.query({
|
|
1424
|
+
__name: 'GetRoleList',
|
|
1425
|
+
roles: {
|
|
1426
|
+
id: true,
|
|
1427
|
+
name: true,
|
|
1428
|
+
description: true,
|
|
1429
|
+
groups: {
|
|
1430
|
+
groupName: true,
|
|
1431
|
+
managers: {
|
|
1432
|
+
fullname: true,
|
|
1433
|
+
email: true
|
|
1434
|
+
},
|
|
1435
|
+
dateAdded: true,
|
|
1436
|
+
dateCreated: true
|
|
1437
|
+
},
|
|
1438
|
+
actions: {
|
|
1439
|
+
update: true,
|
|
1440
|
+
delete: true
|
|
1441
|
+
}
|
|
1442
|
+
}
|
|
1443
|
+
})
|
|
1444
|
+
if (response.roles.length === 0) return undefined
|
|
1445
|
+
const roles = response.roles
|
|
1446
|
+
return roles.map(role => ({ ...role, groups: role.groups.map((group: AccessRoleGroup) => ({
|
|
1447
|
+
...group,
|
|
1448
|
+
dateAdded: DateTime.fromISO(group.dateAdded),
|
|
1449
|
+
dateCreated: group.dateCreated ? DateTime.fromISO(group.dateCreated) : undefined
|
|
1450
|
+
})) }))
|
|
1451
|
+
}
|
|
1452
|
+
|
|
1453
|
+
async getRoleDetails (roleId: string) {
|
|
1454
|
+
const response = await this.client.query({
|
|
1455
|
+
__name: 'GetRoleDetails',
|
|
1456
|
+
roles: {
|
|
1457
|
+
__args: { filter: { ids: [roleId] } },
|
|
1458
|
+
id: true,
|
|
1459
|
+
name: true,
|
|
1460
|
+
description: true,
|
|
1461
|
+
groups: {
|
|
1462
|
+
groupName: true,
|
|
1463
|
+
managers: {
|
|
1464
|
+
fullname: true,
|
|
1465
|
+
email: true
|
|
1466
|
+
},
|
|
1467
|
+
dateAdded: true,
|
|
1468
|
+
dateCreated: true
|
|
1469
|
+
},
|
|
1470
|
+
grants: {
|
|
1471
|
+
id: true,
|
|
1472
|
+
controlGroup: {
|
|
1473
|
+
name: true,
|
|
1474
|
+
title: true,
|
|
1475
|
+
description: true
|
|
1476
|
+
},
|
|
1477
|
+
allow: true,
|
|
1478
|
+
controls: true,
|
|
1479
|
+
tags: {
|
|
1480
|
+
category: true,
|
|
1481
|
+
categoryLabel: true,
|
|
1482
|
+
tag: true,
|
|
1483
|
+
label: true,
|
|
1484
|
+
description: true
|
|
1485
|
+
},
|
|
1486
|
+
actions: {
|
|
1487
|
+
update: true,
|
|
1488
|
+
delete: true
|
|
1489
|
+
}
|
|
1490
|
+
},
|
|
1491
|
+
actions: {
|
|
1492
|
+
update: true,
|
|
1493
|
+
delete: true
|
|
1494
|
+
}
|
|
1495
|
+
}
|
|
1496
|
+
})
|
|
1497
|
+
if (!response.roles.length) return undefined
|
|
1498
|
+
const role = response.roles[0]
|
|
1499
|
+
return { ...role, groups: role.groups.map((group: AccessRoleGroup) => ({
|
|
1500
|
+
...group,
|
|
1501
|
+
dateAdded: DateTime.fromISO(group.dateAdded),
|
|
1502
|
+
dateCreated: group.dateCreated ? DateTime.fromISO(group.dateCreated) : undefined
|
|
1503
|
+
})) }
|
|
1504
|
+
}
|
|
1505
|
+
|
|
1506
|
+
async getAuthorizationInfo () {
|
|
1507
|
+
const response = await this.client.query({
|
|
1508
|
+
__name: 'GetAuthorizationInfo',
|
|
1509
|
+
controlGroups: {
|
|
1510
|
+
name: true,
|
|
1511
|
+
title: true,
|
|
1512
|
+
description: true,
|
|
1513
|
+
tags: {
|
|
1514
|
+
category: true,
|
|
1515
|
+
label: true,
|
|
1516
|
+
description: true,
|
|
1517
|
+
listable: true,
|
|
1518
|
+
tags: {
|
|
1519
|
+
value: true,
|
|
1520
|
+
label: true,
|
|
1521
|
+
description: true
|
|
1522
|
+
}
|
|
1523
|
+
},
|
|
1524
|
+
controls: {
|
|
1525
|
+
name: true,
|
|
1526
|
+
description: true
|
|
1527
|
+
}
|
|
1528
|
+
}
|
|
1529
|
+
})
|
|
1530
|
+
return response.controlGroups
|
|
1531
|
+
}
|
|
1532
|
+
|
|
1533
|
+
async upsertRole (roleId: string | undefined, role: AccessRoleInput, validateOnly: boolean, copyRoleId?: string) {
|
|
1534
|
+
if (roleId != null) {
|
|
1535
|
+
const response = await this.client.mutation({
|
|
1536
|
+
__name: 'UpdateRole',
|
|
1537
|
+
roleUpdate: {
|
|
1538
|
+
__args: { roleId, role, validateOnly },
|
|
1539
|
+
success: true,
|
|
1540
|
+
messages: {
|
|
1541
|
+
message: true,
|
|
1542
|
+
type: true,
|
|
1543
|
+
arg: true
|
|
1544
|
+
}
|
|
1545
|
+
}
|
|
1546
|
+
})
|
|
1547
|
+
return this.mutationForDialog(response.roleUpdate)
|
|
1548
|
+
} else {
|
|
1549
|
+
const response = await this.client.mutation({
|
|
1550
|
+
__name: 'CreateRole',
|
|
1551
|
+
roleCreate: {
|
|
1552
|
+
__args: { role, copyRoleId, validateOnly },
|
|
1553
|
+
success: true,
|
|
1554
|
+
messages: {
|
|
1555
|
+
message: true,
|
|
1556
|
+
type: true,
|
|
1557
|
+
arg: true
|
|
1558
|
+
}
|
|
1559
|
+
}
|
|
1560
|
+
})
|
|
1561
|
+
return this.mutationForDialog(response.roleCreate)
|
|
1562
|
+
}
|
|
1563
|
+
}
|
|
1564
|
+
|
|
1565
|
+
async deleteRole (roleId: string) {
|
|
1566
|
+
const response = await this.client.mutation({
|
|
1567
|
+
__name: 'DeleteRole',
|
|
1568
|
+
roleDelete: {
|
|
1569
|
+
__args: { roleId },
|
|
1570
|
+
success: true
|
|
1571
|
+
}
|
|
1572
|
+
})
|
|
1573
|
+
return response.roleDelete.success
|
|
1574
|
+
}
|
|
1575
|
+
|
|
1576
|
+
async updateGrant (grantId: string, grant: AccessRoleGrantUpdate, validateOnly: boolean) {
|
|
1577
|
+
const response = await this.client.mutation({
|
|
1578
|
+
__name: 'UpdateGrant',
|
|
1579
|
+
roleUpdateGrant: {
|
|
1580
|
+
__args: { grantId, grant, validateOnly },
|
|
1581
|
+
success: true,
|
|
1582
|
+
messages: {
|
|
1583
|
+
message: true,
|
|
1584
|
+
type: true,
|
|
1585
|
+
arg: true
|
|
1586
|
+
}
|
|
1587
|
+
}
|
|
1588
|
+
})
|
|
1589
|
+
return this.mutationForDialog(response.roleUpdateGrant)
|
|
1590
|
+
}
|
|
1591
|
+
|
|
1592
|
+
async createGrant (roleId: string, grant: AccessRoleGrantCreate, validateOnly: boolean) {
|
|
1593
|
+
const response = await this.client.mutation({
|
|
1594
|
+
__name: 'CreateGrant',
|
|
1595
|
+
roleAddGrant: {
|
|
1596
|
+
__args: { roleId, grant, validateOnly },
|
|
1597
|
+
success: true,
|
|
1598
|
+
messages: {
|
|
1599
|
+
message: true,
|
|
1600
|
+
type: true,
|
|
1601
|
+
arg: true
|
|
1602
|
+
}
|
|
1603
|
+
}
|
|
1604
|
+
})
|
|
1605
|
+
return this.mutationForDialog(response.roleAddGrant)
|
|
1606
|
+
}
|
|
1607
|
+
|
|
1608
|
+
async deleteGrant (grantId: string) {
|
|
1609
|
+
const response = await this.client.mutation({
|
|
1610
|
+
__name: 'DeleteGrant',
|
|
1611
|
+
roleDeleteGrant: {
|
|
1612
|
+
__args: { grantId },
|
|
1613
|
+
success: true,
|
|
1614
|
+
messages: {
|
|
1615
|
+
message: true,
|
|
1616
|
+
type: true,
|
|
1617
|
+
arg: true
|
|
1618
|
+
}
|
|
1619
|
+
}
|
|
1620
|
+
})
|
|
1621
|
+
return this.mutationForDialog(response.roleDeleteGrant)
|
|
1622
|
+
}
|
|
1623
|
+
|
|
1624
|
+
async getAnnouncement (active?: boolean) {
|
|
1625
|
+
const response = await this.client.query({
|
|
1626
|
+
__name: 'announcement',
|
|
1627
|
+
announcements:{
|
|
1628
|
+
__args: {
|
|
1629
|
+
filter: active == null ? {} : { active }
|
|
1630
|
+
},
|
|
1631
|
+
id: true,
|
|
1632
|
+
isActive: true,
|
|
1633
|
+
subject: true,
|
|
1634
|
+
body: true,
|
|
1635
|
+
start: true,
|
|
1636
|
+
end: true,
|
|
1637
|
+
link: true,
|
|
1638
|
+
linkText: true,
|
|
1639
|
+
enabled: true,
|
|
1640
|
+
type: true
|
|
1641
|
+
}
|
|
1642
|
+
})
|
|
1643
|
+
|
|
1644
|
+
return response.announcements.length ? response.announcements[0] : undefined
|
|
1645
|
+
}
|
|
1646
|
+
|
|
1647
|
+
async createAnnouncement (announcement: any, validateOnly = true) {
|
|
1648
|
+
const response = await this.client.mutation({
|
|
1649
|
+
__name: 'CreateAnnouncement',
|
|
1650
|
+
createAnnouncement: {
|
|
1651
|
+
__args: {
|
|
1652
|
+
announcement,
|
|
1653
|
+
validateOnly
|
|
1654
|
+
},
|
|
1655
|
+
success: true,
|
|
1656
|
+
messages: {
|
|
1657
|
+
message: true,
|
|
1658
|
+
type: true,
|
|
1659
|
+
arg: true
|
|
1660
|
+
}
|
|
1661
|
+
}
|
|
1662
|
+
})
|
|
1663
|
+
|
|
1664
|
+
return this.mutationForDialog(response.createAnnouncement)
|
|
1665
|
+
}
|
|
1666
|
+
|
|
1667
|
+
async updateAnnouncement (announcementId: string, announcement: any, validateOnly = true) {
|
|
1668
|
+
const response = await this.client.mutation({
|
|
1669
|
+
__name: 'UpdateAnnouncement',
|
|
1670
|
+
updateAnnouncement: {
|
|
1671
|
+
__args: {
|
|
1672
|
+
announcementId,
|
|
1673
|
+
announcement,
|
|
1674
|
+
validateOnly
|
|
1675
|
+
},
|
|
1676
|
+
success: true,
|
|
1677
|
+
messages: {
|
|
1678
|
+
message: true,
|
|
1679
|
+
type: true,
|
|
1680
|
+
arg: true
|
|
1681
|
+
}
|
|
1682
|
+
}
|
|
1683
|
+
})
|
|
1684
|
+
|
|
1685
|
+
return this.mutationForDialog(response.updateAnnouncement)
|
|
1686
|
+
}
|
|
1687
|
+
|
|
1688
|
+
async deleteAnnouncement (id: string) {
|
|
1689
|
+
const response = await this.client.mutation({
|
|
1690
|
+
__name: 'CreateAnnouncement',
|
|
1691
|
+
deleteAnnouncement: {
|
|
1692
|
+
__args: {
|
|
1693
|
+
announcementId: id
|
|
1694
|
+
},
|
|
1695
|
+
success: true,
|
|
1696
|
+
messages: {
|
|
1697
|
+
message: true,
|
|
1698
|
+
type: true,
|
|
1699
|
+
arg: true
|
|
1700
|
+
}
|
|
1701
|
+
}
|
|
1702
|
+
})
|
|
1703
|
+
return response.deleteAnnouncement.success
|
|
1704
|
+
}
|
|
1705
|
+
}
|
|
1706
|
+
|
|
1707
|
+
export const api = new API(PUBLIC_API_BASE, PUBLIC_AUTH_REDIRECT)
|