Skip to content

Commit fa42939

Browse files
authored
Merge pull request #12 from hmrc/DTR-714_Screen_MA2c-_Agent_contact_details_T2
WIP - Created Agent contact details screen.
2 parents 2c46546 + ef41d2f commit fa42939

18 files changed

+592
-22
lines changed

app/controllers/HomeController.scala

Whitespace-only changes.

app/controllers/manageAgents/AgentContactDetailsController.scala

Lines changed: 49 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,22 +20,62 @@ import controllers.actions.IdentifierAction
2020
import models.Mode
2121
import javax.inject.{Inject, Singleton}
2222
import play.api.i18n.I18nSupport
23+
import controllers.JourneyRecoveryController
24+
import controllers.actions.*
25+
import forms.manageAgents.AgentContactDetailsFormProvider
26+
import models.manageAgents.AgentContactDetails
27+
28+
import javax.inject.Inject
29+
import navigation.Navigator
30+
import models.Mode
31+
import pages.manageAgents.{AgentContactDetailsPage, AgentCheckYourAnswersPage}
32+
import play.api.i18n.{I18nSupport, MessagesApi}
2333
import play.api.mvc.{Action, AnyContent, MessagesControllerComponents}
34+
import repositories.SessionRepository
2435
import uk.gov.hmrc.play.bootstrap.frontend.controller.FrontendBaseController
25-
import views.html.IndexView
36+
import views.html.manageAgents.AgentContactDetailsView
37+
38+
import scala.concurrent.{ExecutionContext, Future}
39+
2640

2741
@Singleton
2842
class AgentContactDetailsController @Inject()(
29-
val controllerComponents: MessagesControllerComponents,
43+
override val messagesApi: MessagesApi,
44+
sessionRepository: SessionRepository,
45+
navigator: Navigator,
3046
identify: IdentifierAction,
31-
view: IndexView
32-
) extends FrontendBaseController with I18nSupport {
47+
getData: DataRetrievalAction,
48+
requireData: DataRequiredAction,
49+
formProvider: AgentContactDetailsFormProvider,
50+
val controllerComponents: MessagesControllerComponents,
51+
view: AgentContactDetailsView
52+
)(implicit ec: ExecutionContext) extends FrontendBaseController with I18nSupport {
3353

34-
def onPageLoad(mode: Mode): Action[AnyContent] = identify { implicit request =>
35-
Ok(view())
54+
val form = formProvider()
55+
56+
def onPageLoad(mode: Mode): Action[AnyContent] = (identify andThen getData andThen requireData) {
57+
implicit request =>
58+
59+
val preparedForm = request.userAnswers.get(AgentContactDetailsPage) match {
60+
case None => form
61+
case Some(value) => form.fill(value)
62+
}
63+
64+
Ok(view(preparedForm, mode))
3665
}
3766

38-
def onSubmit(mode: Mode): Action[AnyContent] = identify { implicit request =>
39-
Ok(view())
67+
def onSubmit(mode: Mode): Action[AnyContent] = (identify andThen getData andThen requireData).async {
68+
implicit request =>
69+
70+
form.bindFromRequest().fold(
71+
formWithErrors =>
72+
Future.successful(BadRequest(view(formWithErrors, mode))),
73+
74+
value =>
75+
for {
76+
updatedAnswers <- Future.fromTry(request.userAnswers.set(AgentContactDetailsPage, value))
77+
_ <- sessionRepository.set(updatedAnswers)
78+
} yield Redirect(navigator.nextPage(AgentCheckYourAnswersPage, mode, updatedAnswers))
79+
)
4080
}
41-
}
81+
}

app/controllers/manageAgents/CheckYourAnswersController.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ class CheckYourAnswersController @Inject()(
4747
rows = Seq(
4848
AgentNameSummary.row(request.userAnswers),
4949
AddressSummary.row(request.userAnswers),
50-
ContactTelephoneNumberSummary.row(request.userAnswers),
50+
ContactPhoneNumberSummary.row(request.userAnswers),
5151
ContactEmailSummary.row(request.userAnswers)
5252
).flatten
5353
)
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/*
2+
* Copyright 2025 HM Revenue & Customs
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package forms.constraints
18+
19+
import play.api.data.validation._
20+
21+
object OptionalMaxLength {
22+
def apply(max: Int, errorKey: String): Constraint[Option[String]] =
23+
Constraint("constraints.maxLength") {
24+
case Some(value) if value.length > max =>
25+
Invalid(ValidationError(errorKey, max))
26+
case _ => Valid
27+
}
28+
}
29+
30+
object OptionalEmailFormat {
31+
def apply(errorKey: String): Constraint[Option[String]] =
32+
Constraint("constraints.email") {
33+
case Some(value) if !value.matches("^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$") =>
34+
Invalid(ValidationError(errorKey))
35+
case _ => Valid
36+
}
37+
}
38+
39+
object OptionalPhoneFormat {
40+
def apply(errorKey: String): Constraint[Option[String]] =
41+
Constraint("constraints.phone") {
42+
case Some(value) if !value.matches("""^\d+$""") =>
43+
Invalid(ValidationError(errorKey))
44+
case _ => Valid
45+
}
46+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/*
2+
* Copyright 2025 HM Revenue & Customs
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package forms.manageAgents
18+
19+
import forms.constraints.{OptionalEmailFormat, OptionalMaxLength, OptionalPhoneFormat}
20+
import models.manageAgents.AgentContactDetails
21+
import play.api.data.Form
22+
import play.api.data.Forms.{mapping, optional, text}
23+
import play.api.data.validation.Constraint
24+
25+
class AgentContactDetailsFormProvider {
26+
27+
def apply(): Form[AgentContactDetails] = {
28+
Form(
29+
mapping(
30+
"phone" -> optional(text)
31+
.verifying(OptionalMaxLength(14, "manageAgents.agentContactDetails.error.phoneLength"))
32+
.verifying(OptionalPhoneFormat("manageAgents.agentContactDetails.error.phoneInvalid")),
33+
"email" -> optional(text)
34+
.verifying(OptionalMaxLength(36, "manageAgents.agentContactDetails.error.emailLength"))
35+
.verifying(OptionalEmailFormat("manageAgents.agentContactDetails.error.emailInvalid"))
36+
)(AgentContactDetails.apply)(contactDetails => Some((contactDetails.phone, contactDetails.email)))
37+
)
38+
}
39+
}

app/models/manageAgents/AgentContactDetails.scala

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,12 @@
1616

1717
package models.manageAgents
1818

19-
import play.api.libs.json.*
19+
import play.api.libs.json.{Json, OFormat}
2020

21-
case class AgentContactDetails(contactTelephoneNumber: String, contactEmail: String)
21+
case class AgentContactDetails(
22+
phone: Option[String], email: Option[String]
23+
)
2224

2325
object AgentContactDetails {
24-
25-
implicit val format: OFormat[AgentContactDetails] = Json.format
26+
implicit val format: OFormat[AgentContactDetails] = Json.format[AgentContactDetails]
2627
}

app/pages/manageAgents/AgentContactDetailsPage.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,5 +25,5 @@ case object AgentContactDetailsPage extends QuestionPage[AgentContactDetails] {
2525
override def path: JsPath = JsPath \ toString
2626

2727
override def toString: String = "agentContactDetails"
28-
29-
}
28+
29+
}

app/viewmodels/manageAgents/checkAnswers/ContactEmailSummary.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ object ContactEmailSummary {
3030
answers.get(AgentContactDetailsPage).map { answer =>
3131
SummaryListRowViewModel(
3232
key = s"manageAgents.contactEmail.checkYourAnswersLabel",
33-
value = ValueViewModel(HtmlFormat.escape(answer.contactEmail).toString),
33+
value = ValueViewModel(HtmlFormat.escape(answer.email.getOrElse("")).toString),
3434
actions = Seq(
3535
ActionItemViewModel(
3636
"site.change",

app/viewmodels/manageAgents/checkAnswers/ContactTelephoneNumberSummary.scala renamed to app/viewmodels/manageAgents/checkAnswers/ContactPhoneNumberSummary.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,13 @@ import uk.gov.hmrc.govukfrontend.views.viewmodels.summarylist.SummaryListRow
2424
import viewmodels.govuk.summarylist.*
2525
import viewmodels.implicits.*
2626

27-
object ContactTelephoneNumberSummary {
27+
object ContactPhoneNumberSummary {
2828

2929
def row(answers: UserAnswers)(implicit messages: Messages): Option[SummaryListRow] = {
3030
answers.get(AgentContactDetailsPage).map { answer =>
3131
SummaryListRowViewModel(
3232
key = s"manageAgents.contactTelephoneNumber.checkYourAnswersLabel",
33-
value = ValueViewModel(HtmlFormat.escape(answer.contactTelephoneNumber).toString),
33+
value = ValueViewModel(HtmlFormat.escape(answer.phone.getOrElse("")).toString),
3434
actions = Seq(
3535
ActionItemViewModel(
3636
"site.change",
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
@*
2+
* Copyright 2025 HM Revenue & Customs
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*@
16+
17+
@import viewmodels.InputWidth._
18+
19+
@this(
20+
layout: templates.Layout,
21+
formHelper: FormWithCSRF,
22+
govukErrorSummary: GovukErrorSummary,
23+
govukInput: GovukInput,
24+
govukButton: GovukButton
25+
)
26+
27+
@(form: Form[_], mode: Mode)(implicit request: Request[_], messages: Messages)
28+
29+
@layout(pageTitle = title(form, messages("manageAgents.agentContactDetails.title"))) {
30+
31+
@formHelper(action = controllers.manageAgents.routes.AgentContactDetailsController.onSubmit(mode), Symbol("autoComplete") -> "off") {
32+
33+
@if(form.errors.nonEmpty) {
34+
@govukErrorSummary(ErrorSummaryViewModel(form))
35+
}
36+
37+
38+
<span class="govuk-caption-xl">@messages("manageAgents.agentContactDetails.caption")</span>
39+
<h1 class="govuk-heading-xl">@messages("manageAgents.agentContactDetails.heading")</h1>
40+
41+
@govukInput(
42+
InputViewModel(
43+
field = form("phone"),
44+
label = LabelViewModel(messages("manageAgents.agentContactDetails.question1"))
45+
)
46+
.withWidth(Full)
47+
)
48+
49+
@govukInput(
50+
InputViewModel(
51+
field = form("email"),
52+
label = LabelViewModel(messages("manageAgents.agentContactDetails.question2"))
53+
54+
)
55+
.withWidth(Full)
56+
.withHint(HintViewModel(HtmlContent(messages("manageAgents.agentContactDetails.hint"))))
57+
)
58+
59+
@govukButton(
60+
ButtonViewModel(messages("site.continue"))
61+
)
62+
}
63+
}

0 commit comments

Comments
 (0)