-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathPageController.ts
More file actions
200 lines (172 loc) · 5.13 KB
/
PageController.ts
File metadata and controls
200 lines (172 loc) · 5.13 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
import {
ControllerPath,
type Events,
type FormDefinition,
type Page,
type Section
} from '@defra/forms-model'
import Boom from '@hapi/boom'
import { type Lifecycle, type RouteOptions, type Server } from '@hapi/hapi'
import { type ComponentCollection } from '~/src/server/plugins/engine/components/ComponentCollection.js'
import { type FormComponent } from '~/src/server/plugins/engine/components/FormComponent.js'
import {
getSaveAndExitHelpers,
getStartPath,
normalisePath
} from '~/src/server/plugins/engine/helpers.js'
import { type FormModel } from '~/src/server/plugins/engine/models/index.js'
import { type ExecutableCondition } from '~/src/server/plugins/engine/models/types.js'
import {
type FormContext,
type PageViewModelBase
} from '~/src/server/plugins/engine/types.js'
import {
type FormRequest,
type FormRequestPayload,
type FormRequestPayloadRefs,
type FormRequestRefs,
type FormResponseToolkit
} from '~/src/server/routes/types.js'
export class PageController {
/**
* The base class for all page controllers. Page controllers are responsible for generating the get and post route handlers when a user navigates to `/{id}/{path*}`.
*/
def: FormDefinition
name?: string
model: FormModel
pageDef: Page
title: string
section?: Section
condition?: ExecutableCondition
events?: Events
collection?: ComponentCollection
viewName = 'index'
allowSaveAndExit = false
constructor(model: FormModel, pageDef: Page) {
const { def } = model
this.def = def
this.name = def.name
this.model = model
this.pageDef = pageDef
this.title = pageDef.title
this.events = pageDef.events
// Resolve section
if (pageDef.section) {
this.section = model.getSection(pageDef.section)
}
// Resolve condition
if (pageDef.condition) {
this.condition = model.conditions[pageDef.condition]
}
// Override view name
if (pageDef.view) {
this.viewName = pageDef.view
}
}
get path() {
return this.pageDef.path
}
get href() {
const { path } = this
return this.getHref(`/${normalisePath(path)}`)
}
get keys() {
return this.collection?.keys ?? []
}
/**
* {@link https://hapi.dev/api/?v=20.1.2#route-options}
*/
get getRouteOptions(): RouteOptions<FormRequestRefs> {
return {}
}
/**
* {@link https://hapi.dev/api/?v=20.1.2#route-options}
*/
get postRouteOptions(): RouteOptions<FormRequestPayloadRefs> {
return {}
}
get viewModel(): PageViewModelBase {
const { name, section, title } = this
const showTitle = true
const pageTitle = title
const sectionTitle = section?.hideTitle !== true ? section?.title : ''
return {
name,
page: this,
pageTitle,
sectionTitle,
showTitle,
isStartPage: false,
serviceUrl: this.getHref('/'),
feedbackLink: this.feedbackLink,
phaseTag: this.phaseTag
}
}
get feedbackLink() {
return this.def.options?.disableUserFeedback
? undefined
: `/form/feedback?formId=${this.model.formId}`
}
get phaseTag() {
const { def } = this
return def.phaseBanner?.phase
}
getHref(path: string): string {
const basePath = this.model.basePath
if (path === '/') {
return `/${basePath}`
}
// if ever the path is not prefixed with a slash, add it
const relativeTargetPath = path.startsWith('/') ? path.substring(1) : path
let finalPath = `/${basePath}`
if (relativeTargetPath) {
finalPath += `/${relativeTargetPath}`
}
finalPath = finalPath.replace(/\/{2,}/g, '/')
return finalPath
}
getStartPath() {
return getStartPath(this.model)
}
getSummaryPath() {
return ControllerPath.Summary.valueOf()
}
getStatusPath() {
return ControllerPath.Status.valueOf()
}
makeGetRouteHandler(): (
request: FormRequest,
context: FormContext,
h: FormResponseToolkit
) => ReturnType<Lifecycle.Method<FormRequestRefs>> {
return (request, context, h) => {
const { viewModel, viewName } = this
return h.view(viewName, viewModel)
}
}
makePostRouteHandler(): (
request: FormRequestPayload,
context: FormContext,
h: FormResponseToolkit
) => ReturnType<Lifecycle.Method<FormRequestPayloadRefs>> {
throw Boom.badRequest('Unsupported POST route handler for this page')
}
/**
* Get supplementary state keys for clearing component state.
*
* This method returns page controller-level state keys only. The core component's
* state key (the component's name) is managed separately by the framework and should
* NOT be included in the returned array.
*
* Returns an empty array by default. Override in subclasses to provide
* page-specific supplementary state keys (e.g., upload state, cached data).
* @param _component - The component to get supplementary state keys for (optional)
* @returns Array of supplementary state keys to clear (excluding the component name itself)
*/
getStateKeys(_component?: FormComponent): string[] {
return []
}
shouldShowSaveAndExit(server: Server): boolean {
return getSaveAndExitHelpers(server) !== undefined && this.allowSaveAndExit
}
}