From a4794a6fdde79c43e89f3c4b5fccf0df78aaa89f Mon Sep 17 00:00:00 2001 From: Rich Birch Date: Wed, 9 Oct 2024 13:23:01 +0100 Subject: [PATCH 01/12] DRTII-1604 Add page titles to Route locations --- .../src/main/scala/drt/client/SPAMain.scala | 68 ++++-- .../drt/client/components/ContactPage.scala | 203 ------------------ 2 files changed, 50 insertions(+), 221 deletions(-) delete mode 100644 client/src/main/scala/drt/client/components/ContactPage.scala diff --git a/client/src/main/scala/drt/client/SPAMain.scala b/client/src/main/scala/drt/client/SPAMain.scala index d89cea143..ffe4145e6 100644 --- a/client/src/main/scala/drt/client/SPAMain.scala +++ b/client/src/main/scala/drt/client/SPAMain.scala @@ -4,7 +4,7 @@ import diode.Action import drt.client.actions.Actions._ import drt.client.components.TerminalDesksAndQueues.{ChartsView, Deployments, DeskType, DisplayType, Ideal, TableView} import drt.client.components.styles._ -import drt.client.components.{ContactPage, FeedsStatusPage, ForecastUploadComponent, GlobalStyles, Layout, PortConfigPage, PortDashboardPage, TerminalComponent, TrainingHubComponent, UserDashboardPage} +import drt.client.components.{FeedsStatusPage, ForecastUploadComponent, GlobalStyles, Layout, PortConfigPage, PortDashboardPage, TerminalComponent, TrainingHubComponent, UserDashboardPage} import drt.client.logger._ import drt.client.services.JSDateConversions.SDate import drt.client.services._ @@ -15,10 +15,12 @@ import io.kinoplan.scalajs.react.material.ui.core.system.ThemeProvider import japgolly.scalajs.react.Callback import japgolly.scalajs.react.extra.router._ import org.scalajs.dom -import org.scalajs.dom.console +import org.scalajs.dom.{console, window} import scalacss.ProdDefaults._ import uk.gov.homeoffice.drt.Urls +import uk.gov.homeoffice.drt.ports.PortCode import uk.gov.homeoffice.drt.ports.Terminals.Terminal +import uk.gov.homeoffice.drt.ports.config.AirportConfigs import uk.gov.homeoffice.drt.time.{LocalDate, SDateLike} import scala.scalajs.js.annotation.{JSExport, JSExportTopLevel} @@ -26,7 +28,24 @@ import scala.util.Try object SPAMain { - sealed trait Loc + sealed trait Loc { + val portCodeStr = dom.document.getElementById("port-code").getAttribute("value") + val portConfig = AirportConfigs.confByPort(PortCode(portCodeStr)) + val url = window.location.href + + def terminalPart(maybeTerminal: Option[Terminal]): String = { + val terminalShortName = maybeTerminal.map { t => + val terminalStr = t.toString + if (terminalStr.take(1) == "T") terminalStr.drop(1) else terminalStr + } + terminalShortName.map(t => s", Terminal $t").getOrElse("") + } + + def title(pageName: String, maybeTerminal: Option[Terminal]) = + s"$pageName at ${portConfig.portCode.iata} (${portConfig.portName})${terminalPart(maybeTerminal)} - DRT" + + def title(maybeTerminal: Option[Terminal]): String + } sealed trait UrlParameter { val name: String @@ -89,6 +108,16 @@ object SPAMain { subMode: String = "arrivals", queryParams: Map[String, String] = Map.empty[String, String] ) extends Loc { + val pageName = subMode match { + case "arrivals" => "Arrivals" + case "desksAndQueues" => "Desks and queues" + case "staffing" => "Monthly staffing" + case "simulations" => "Simulate day" + case other => "" + } + + override def title(maybeTerminal: Option[Terminal]): String = title(pageName, maybeTerminal) + val terminal: Terminal = Terminal(terminalName) val maybeViewDate: Option[LocalDate] = queryParams.get(UrlDateParameter.paramName) .filter(_.matches(".+")) @@ -150,19 +179,29 @@ object SPAMain { def serverLogEndpoint: String = absoluteUrl("logging") - case class PortDashboardLoc(period: Option[Int]) extends Loc - - case object StatusLoc extends Loc + case class PortDashboardLoc(period: Option[Int]) extends Loc { + override def title(maybeTerminal: Option[Terminal]): String = title("Dashboard", maybeTerminal) + } - case object UserDashboardLoc extends Loc + case object StatusLoc extends Loc { + override def title(maybeTerminal: Option[Terminal]): String = title("Feeds status", maybeTerminal) + } - case object ContactUsLoc extends Loc + case object UserDashboardLoc extends Loc { + override def title(maybeTerminal: Option[Terminal]): String = title("Dashboard", maybeTerminal) + } - case class TrainingHubLoc(modeStr: String = "dropInBooking") extends Loc + case class TrainingHubLoc(modeStr: String = "dropInBooking") extends Loc { + override def title(maybeTerminal: Option[Terminal]): String = title("Training hub", maybeTerminal) + } - case object PortConfigLoc extends Loc + case object PortConfigLoc extends Loc { + override def title(maybeTerminal: Option[Terminal]): String = title("Port config", maybeTerminal) + } - case object ForecastFileUploadLoc extends Loc + case object ForecastFileUploadLoc extends Loc { + override def title(maybeTerminal: Option[Terminal]): String = title("Forecast upload", maybeTerminal) + } private val initialRequestsActions = Seq( GetApplicationVersion, @@ -195,7 +234,6 @@ object SPAMain { dashboardRoute(dsl) | terminalRoute(dsl) | statusRoute(dsl) | - contactRoute(dsl) | trainingHubRoute(dsl) | portConfigRoute(dsl) | forecastFileUploadRoute(dsl) @@ -232,12 +270,6 @@ object SPAMain { staticRoute("#status", StatusLoc) ~> renderR(_ => proxy(p => FeedsStatusPage(p()._1, p()._2))) } - def contactRoute(dsl: RouterConfigDsl[Loc, Unit]): dsl.Rule = { - import dsl._ - - staticRoute("#contact", ContactUsLoc) ~> renderR(_ => ContactPage()) - } - def forecastFileUploadRoute(dsl: RouterConfigDsl[Loc, Unit]): dsl.Rule = { import dsl._ diff --git a/client/src/main/scala/drt/client/components/ContactPage.scala b/client/src/main/scala/drt/client/components/ContactPage.scala deleted file mode 100644 index 76d1ddb23..000000000 --- a/client/src/main/scala/drt/client/components/ContactPage.scala +++ /dev/null @@ -1,203 +0,0 @@ -package drt.client.components - -import diode.data.Pot -import drt.client.SPAMain -import drt.client.components.styles.DrtTheme -import drt.client.modules.GoogleEventTracker -import drt.client.services.SPACircuit -import drt.client.services.handlers.GetABFeature -import drt.shared.{ContactDetails, OutOfHoursStatus} -import io.kinoplan.scalajs.react.material.ui.core.system.SxProps -import io.kinoplan.scalajs.react.material.ui.core.{MuiButton, MuiGrid, MuiPaper, MuiTypography} -import japgolly.scalajs.react.component.Scala.Component -import japgolly.scalajs.react.vdom.html_<^._ -import japgolly.scalajs.react.{Callback, CtorType, ScalaComponent} -import org.scalajs.dom -import uk.gov.homeoffice.drt.ABFeature -import drt.client.components.styles.DrtTheme._ - -object ContactPage { - - case class Props() - - val component: Component[Props, Unit, Unit, CtorType.Props] = ScalaComponent.builder[Props]("ContactUs") - .render_P(_ => - MuiGrid(container = true)( - MuiGrid(item = true, xs = 6)( - <.div(^.className := "contact-us", ContactDetailsComponent())), - MuiGrid(item = true, xs = 6)( - <.div(FeedBackComponent()) - ) - )) - .componentDidMount(_ => Callback { - GoogleEventTracker.sendPageView(s"contact") - }) - .build - - def apply(): VdomElement = component(Props()) -} - -case class ContactModel(contactDetails: Pot[ContactDetails], oohStatus: Pot[OutOfHoursStatus]) - -object ContactDetailsComponent { - - case class Props() - - val component: Component[Props, Unit, Unit, CtorType.Props] = ScalaComponent.builder[Props]("ContactUs") - .render_P { _ => - val contactDetailsRCP = SPACircuit.connect(m => ContactModel(m.contactDetails, m.oohStatus)) - <.div( - contactDetailsRCP(contactDetailsMP => { - <.div(contactDetailsMP().contactDetails.renderReady(details => { - contactDetailsMP().oohStatus.renderReady(oohStatus => { - val email = details.supportEmail.getOrElse("DRT Support Email Missing") - val oohPhone = details.oohPhone.getOrElse("OOH Contact Number Missing") - val contactUsHeader = MuiGrid(item = true, xs = 12)(MuiTypography(sx = SxProps(Map( - "color" -> DrtTheme.theme.palette.primary.`700`, - "fontSize" -> DrtTheme.theme.typography.h2.fontSize, - "fontWeight" -> DrtTheme.theme.typography.h2.fontWeight - )))("Contacting the DRT team")) - - val emailHeader = "Email :" - val officeHourLabel = "Office hours :" - val officeHours = "Monday to Friday (9 am to 5 pm)" - - val oohMesssage = MuiGrid(container = true, spacing = 2)( - contactUsHeader, - MuiGrid(item = true, xs = 2)( - MuiTypography(variant = "h7", sx = SxProps(Map("fontWeight" -> "bold")))( - officeHourLabel - ) - ), - MuiGrid(item = true, xs = 10)( - MuiTypography(variant = "h7", sx = SxProps(Map("float" -> "left")))( - officeHours - ) - ), - MuiGrid(item = true, xs = 2)( - MuiTypography(variant = "h7", sx = SxProps(Map("fontWeight" -> "bold")))( - emailHeader - ) - ), - MuiGrid(item = true, xs = 10)( - MuiTypography(variant = "h7", sx = SxProps(Map("float" -> "left")))( - <.a(^.href := s"mailto:$email", email) - ) - ), - MuiGrid(item = true, xs = 6)( - MuiTypography(variant = "h7", sx = SxProps(Map("fontWeight" -> "bold")))( - "Contact number (outside of office hours) :" - ) - ), - MuiGrid(item = true, xs = 6)( - MuiTypography(variant = "h7", sx = SxProps(Map("float" -> "left")))( - oohPhone - ) - ), - ) - - val inHoursMessage = MuiGrid(container = true, spacing = 2)( - contactUsHeader, - MuiGrid(item = true, xs = 2)( - MuiTypography(variant = "h7", sx = SxProps(Map("fontWeight" -> "bold")))( - officeHourLabel - ) - ), - MuiGrid(item = true, xs = 10)( - MuiTypography(variant = "h7", sx = SxProps(Map("float" -> "left")))( - officeHours - ) - ), - MuiGrid(item = true, xs = 2)( - MuiTypography(variant = "h7", sx = SxProps(Map("fontWeight" -> "bold")))( - emailHeader - ) - ), - MuiGrid(item = true, xs = 10)( - MuiTypography(variant = "h7", sx = SxProps(Map("float" -> "left")))( - <.a(^.href := s"mailto:$email", email) - ) - ) - ) - - <.div(if (oohStatus.isOoh) oohMesssage else inHoursMessage) - }) - })) - }) - ) - }.build - - def apply(): VdomElement = component(Props()) - -} - -case class FeedbackModel(abFeatures: Pot[Seq[ABFeature]]) - -object FeedBackComponent { - - case class Props() - - val component: Component[Props, Unit, Unit, CtorType.Props] = ScalaComponent.builder[Props]("ContactUs") - .render_P { _ => - val feedbackRCP = SPACircuit.connect(m => FeedbackModel(m.abFeatures)) - <.div( - feedbackRCP(feedbackMP => { - <.div( - feedbackMP().abFeatures.renderReady { abFeatures => - val aORbTest = abFeatures.headOption.map(_.abVersion).getOrElse("B") - val bannerHead = aORbTest match { - case "A" => "Your feedback improves DRT for everyone" - case _ => "Help us improve the DRT experience" - } - <.div( - MuiPaper(sx = SxProps(Map("elevation" -> "4", "padding" -> "16px", "margin" -> "20px", "backgroundColor" -> "#0E2560")))( - MuiGrid(container = true, spacing = 2)( - MuiGrid(item = true, xs = 12)( - MuiTypography(variant = "h4", sx = SxProps(Map("color" -> "white", "fontWeight" -> "bold")))( - bannerHead - ) - ), - MuiGrid(item = true, xs = 12)( - MuiTypography(variant = "h7", sx = SxProps(Map("color" -> "white", "padding" -> "2px 0")))( - "Complete a short survey (takes 2 minutes to complete)" - ) - ), - MuiGrid(item = true, xs = 12)( - MuiGrid(container = true, direction = "column")( - MuiGrid(item = true, xs = 12)( - MuiTypography(variant = "h7", - sx = SxProps(Map("color" -> "white", "padding" -> "2px 0", "fontWeight" -> "bold")))( - "Your feedback improves how our data can:" - ) - ), - MuiGrid(item = true, xs = 12)( - MuiTypography(variant = "h7", sx = SxProps(Map("color" -> "white", "padding" -> "0px 0")))( - <.ul( - <.li("support resource planning capability"), - <.li("facilitate smoother journeys for legitimate passengers"), - <.li("identify potential risks"), - <.li("create a more resilient Border") - )) - ) - ) - ), - MuiGrid(item = true, xs = 12)( - MuiButton(variant = "outlined", sx = SxProps(Map("textTransform" -> "none", - "border" -> "1px solid white", - "color" -> "white", - "fontWeight" -> "bold", - "fontSize" -> buttonTheme.typography.fontSize)))( - "Give feedback", ^.onClick --> Callback(dom.window.open(s"${SPAMain.urls.rootUrl}/feedback/contact-us/$aORbTest", "_blank")), - ) - ) - ))) - }) - })) - }.componentDidMount(_ => - Callback(SPACircuit.dispatch(GetABFeature("feedback"))) - ) - .build - - def apply(): VdomElement = component(Props()) - -} From 098cd9b3c0a2e17f92991e14989bc847e759dd91 Mon Sep 17 00:00:00 2001 From: Rich Birch Date: Wed, 9 Oct 2024 13:24:12 +0100 Subject: [PATCH 02/12] DRTII-1604 Set page title in router --- client/src/main/scala/drt/client/SPAMain.scala | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/client/src/main/scala/drt/client/SPAMain.scala b/client/src/main/scala/drt/client/SPAMain.scala index ffe4145e6..5da68a5d6 100644 --- a/client/src/main/scala/drt/client/SPAMain.scala +++ b/client/src/main/scala/drt/client/SPAMain.scala @@ -241,6 +241,15 @@ object SPAMain { rule.notFound(redirectToPage(PortDashboardLoc(None))(SetRouteVia.HistoryReplace)) } .renderWith(Layout(_, _)) + .setTitle { loc: Loc => + val terminalRegex = """.+terminal/([A-Z0-9]+)/.+""".r + val url = window.location.href + val maybeTerminal = url match { + case terminalRegex(t) => Some(Terminal(t)) + case _ => None + } + loc.title(maybeTerminal) + } .onPostRender((maybePrevLoc, currentLoc) => { Callback( (maybePrevLoc, currentLoc) match { From 2c4fcf0ba0ddf802ec1831d6f30ea7a9bdac33e5 Mon Sep 17 00:00:00 2001 From: Rich Birch Date: Wed, 9 Oct 2024 14:41:30 +0100 Subject: [PATCH 03/12] DRTII-1604 Remove old page title code. Add missing terminal page titles --- .../src/main/scala/drt/client/SPAMain.scala | 14 +++++----- .../client/components/FeedsStatusPage.scala | 4 --- .../components/FlightTableContent.scala | 4 --- .../components/ForecastUploadComponent.scala | 5 +--- .../client/components/MonthlyStaffing.scala | 4 --- .../client/components/PortConfigPage.scala | 26 ------------------- .../client/components/PortDashboardPage.scala | 7 ----- .../TerminalDashboardComponent.scala | 4 --- .../components/TerminalDesksAndQueues.scala | 5 +--- .../TerminalPlanningComponent.scala | 4 +-- .../client/components/TerminalStaffing.scala | 3 --- .../components/TrainingHubComponent.scala | 3 --- .../ScenarioSimulationComponent.scala | 6 ----- 13 files changed, 11 insertions(+), 78 deletions(-) diff --git a/client/src/main/scala/drt/client/SPAMain.scala b/client/src/main/scala/drt/client/SPAMain.scala index 5da68a5d6..853cf2646 100644 --- a/client/src/main/scala/drt/client/SPAMain.scala +++ b/client/src/main/scala/drt/client/SPAMain.scala @@ -108,12 +108,14 @@ object SPAMain { subMode: String = "arrivals", queryParams: Map[String, String] = Map.empty[String, String] ) extends Loc { - val pageName = subMode match { - case "arrivals" => "Arrivals" - case "desksAndQueues" => "Desks and queues" - case "staffing" => "Monthly staffing" - case "simulations" => "Simulate day" - case other => "" + val pageName = (modeStr.toLowerCase, subMode.toLowerCase) match { + case ("current", "arrivals") => "Arrivals" + case ("current", "desksAndQueues") => "Desks and queues" + case ("current", "staffing") => "Staff movements" + case ("current", "simulations") => "Simulate day" + case ("planning", _) => "Staff planning" + case ("staffing", _) => "Monthly staffing" + case _ => "" } override def title(maybeTerminal: Option[Terminal]): String = title(pageName, maybeTerminal) diff --git a/client/src/main/scala/drt/client/components/FeedsStatusPage.scala b/client/src/main/scala/drt/client/components/FeedsStatusPage.scala index 787c9a086..d694b041b 100644 --- a/client/src/main/scala/drt/client/components/FeedsStatusPage.scala +++ b/client/src/main/scala/drt/client/components/FeedsStatusPage.scala @@ -158,10 +158,6 @@ object FeedsStatusPage { ) } } - .componentDidMount { p => - Callback(SetDocumentTitle("Feeds status", p.props.airportConfigPot)) >> - Callback(GoogleEventTracker.sendPageView("feed-status")) - } .build private def displayTime(date: MillisSinceEpoch): String = { diff --git a/client/src/main/scala/drt/client/components/FlightTableContent.scala b/client/src/main/scala/drt/client/components/FlightTableContent.scala index 8c5c72ebb..b0826950b 100644 --- a/client/src/main/scala/drt/client/components/FlightTableContent.scala +++ b/client/src/main/scala/drt/client/components/FlightTableContent.scala @@ -171,10 +171,6 @@ object FlightTableContent { val component: Component[Props, Unit, Backend, CtorType.Props] = ScalaComponent.builder[Props]("FlightTableContent") .renderBackend[Backend] - .componentDidMount { p => - Callback(SetDocumentTitle("Arrivals", p.props.terminal, p.props.airportConfig)) >> - Callback(log.info("FlightTableContent mounted")) - } .build def apply(props: Props): VdomElement = component(props) diff --git a/client/src/main/scala/drt/client/components/ForecastUploadComponent.scala b/client/src/main/scala/drt/client/components/ForecastUploadComponent.scala index 2162b0b61..c5c2f15b7 100644 --- a/client/src/main/scala/drt/client/components/ForecastUploadComponent.scala +++ b/client/src/main/scala/drt/client/components/ForecastUploadComponent.scala @@ -73,10 +73,7 @@ object ForecastUploadComponent { } ) } - .componentDidMount { p => - Callback(SetDocumentTitle("Forecast Upload", p.props.airportConfigPot)) >> - Callback(GoogleEventTracker.sendPageView(s"forecastFileUpload")) - }.build + .build def apply(airportConfigPot: Pot[AirportConfig]): VdomElement = component(Props(airportConfigPot)) diff --git a/client/src/main/scala/drt/client/components/MonthlyStaffing.scala b/client/src/main/scala/drt/client/components/MonthlyStaffing.scala index afd82e372..4ee747d6c 100644 --- a/client/src/main/scala/drt/client/components/MonthlyStaffing.scala +++ b/client/src/main/scala/drt/client/components/MonthlyStaffing.scala @@ -243,10 +243,6 @@ object MonthlyStaffing { ) } .configure(Reusability.shouldComponentUpdate) - .componentDidMount { p => - Callback(SetDocumentTitle("Monthly Staffing", p.props.terminalPageTab.terminal, p.props.airportConfig)) >> - Callback(GoogleEventTracker.sendPageView(s"${p.props.terminalPageTab.terminal}/planning/${defaultStartDate(p.props.terminalPageTab.dateFromUrlOrNow).toISODateOnly}/${p.props.terminalPageTab.subMode}")) - } .build def updatedShiftAssignments( diff --git a/client/src/main/scala/drt/client/components/PortConfigPage.scala b/client/src/main/scala/drt/client/components/PortConfigPage.scala index d85f6afd1..19fb214bf 100644 --- a/client/src/main/scala/drt/client/components/PortConfigPage.scala +++ b/client/src/main/scala/drt/client/components/PortConfigPage.scala @@ -60,37 +60,11 @@ object PortConfigPage { ) mp.render(identity) } - .componentDidMount { p => - Callback(SetDocumentTitle("Port Config", p.props.airportConfig)) >> - Callback(GoogleEventTracker.sendPageView(s"port-config")) - } .build def apply(props: Props): VdomElement = component(props) } -object SetDocumentTitle { - def apply(pageName: String, airportConfigPot: Pot[AirportConfig]): Unit = { - document.title = airportConfigPot match { - case Ready(airportConfig) => constructTitle(pageName, airportConfig.portCode, airportConfig.portName, None) - case _ => "Loading..." - } - } - - def apply(pageName: String, airportConfig: AirportConfig): Unit = { - document.title = constructTitle(pageName, airportConfig.portCode, airportConfig.portName, None) - } - - def apply(pageName: String, terminal: Terminal, airportConfig: AirportConfig): Unit = { - document.title = constructTitle(pageName, airportConfig.portCode, airportConfig.portName, Option(terminal)) - } - - private def constructTitle(pageName: String, portCode: PortCode, portName: String, maybeTerminal: Option[Terminal]): String = { - val terminal = maybeTerminal.map(t => s", Terminal ${t.toString}").getOrElse("") - s"$pageName at ${portCode.iata} ($portName)$terminal - DRT" - } -} - object PortConfigDetails { case class Props(airportConfig: AirportConfig, gateStandWalktime: WalkTimes, updatesByTerminal: Map[Terminal, EgateBanksUpdates]) extends UseValueEq diff --git a/client/src/main/scala/drt/client/components/PortDashboardPage.scala b/client/src/main/scala/drt/client/components/PortDashboardPage.scala index cfd0f4a55..5b368d139 100644 --- a/client/src/main/scala/drt/client/components/PortDashboardPage.scala +++ b/client/src/main/scala/drt/client/components/PortDashboardPage.scala @@ -117,13 +117,6 @@ object PortDashboardPage { })) } }) - .componentWillReceiveProps(p => Callback { - GoogleEventTracker.sendPageView(s"dashboard${p.nextProps.dashboardPage.period.map(period => s"/$period").getOrElse("")}") - }) - .componentDidMount { p => - Callback(SetDocumentTitle("Dashboard", p.props.airportConfig)) >> - Callback(GoogleEventTracker.sendPageView(s"dashboard${p.props.dashboardPage.period.map(period => s"/$period").getOrElse("")}")) - } .build def apply(router: RouterCtl[Loc], dashboardPage: PortDashboardLoc = PortDashboardLoc(None), airportConfig: Pot[AirportConfig]): VdomElement = component(Props(router, dashboardPage, airportConfig)) diff --git a/client/src/main/scala/drt/client/components/TerminalDashboardComponent.scala b/client/src/main/scala/drt/client/components/TerminalDashboardComponent.scala index aa77ea5c8..daeff8f19 100644 --- a/client/src/main/scala/drt/client/components/TerminalDashboardComponent.scala +++ b/client/src/main/scala/drt/client/components/TerminalDashboardComponent.scala @@ -189,10 +189,6 @@ object TerminalDashboardComponent { } <.div(pot.render(identity)) } - .componentDidMount { p => - Callback(SetDocumentTitle("Terminal Dashboard", p.props.terminalPageTabLoc.terminal, p.props.airportConfig)) >> - Callback(GoogleEventTracker.sendPageView(page = s"terminal-dashboard-${p.props.terminalPageTabLoc.terminal}")) - } .build private def cmsForTerminalAndQueue(crunchMinutes: SortedMap[TQM, CrunchMinute], queue: Queue, terminal: Terminal): Iterable[CrunchMinute] = diff --git a/client/src/main/scala/drt/client/components/TerminalDesksAndQueues.scala b/client/src/main/scala/drt/client/components/TerminalDesksAndQueues.scala index a59ad357f..37fd6531e 100644 --- a/client/src/main/scala/drt/client/components/TerminalDesksAndQueues.scala +++ b/client/src/main/scala/drt/client/components/TerminalDesksAndQueues.scala @@ -299,10 +299,7 @@ object TerminalDesksAndQueues { showWaitColumn = !p.featureFlags.displayWaitTimesToggle) } .renderBackend[Backend] - .componentDidMount { p => - Callback(SetDocumentTitle("Desks and Queues", p.props.terminal, p.props.airportConfig)) >> - StickyTableHeader("[data-sticky]") - } + .componentDidMount(_ => StickyTableHeader("[data-sticky]")) .build def documentScrollTop: Double = Math.max(dom.document.documentElement.scrollTop, dom.document.body.scrollTop) diff --git a/client/src/main/scala/drt/client/components/TerminalPlanningComponent.scala b/client/src/main/scala/drt/client/components/TerminalPlanningComponent.scala index adfd7f335..38c2f631b 100644 --- a/client/src/main/scala/drt/client/components/TerminalPlanningComponent.scala +++ b/client/src/main/scala/drt/client/components/TerminalPlanningComponent.scala @@ -205,9 +205,7 @@ object TerminalPlanningComponent { } .configure(Reusability.shouldComponentUpdate) .componentDidMount { p => - Callback(SetDocumentTitle("Staff Planning", p.props.page.terminal, p.props.airportConfig)) >> - Callback(SPACircuit.dispatch(GetForecastWeek(p.props.page.dateFromUrlOrNow, Terminal(p.props.page.terminalName), p.props.timePeriod))) >> - Callback(GoogleEventTracker.sendPageView(s"${p.props.page.terminal}/planning/${defaultStartDate(p.props.page.dateFromUrlOrNow).toISODateOnly}")) + Callback(SPACircuit.dispatch(GetForecastWeek(p.props.page.dateFromUrlOrNow, Terminal(p.props.page.terminalName), p.props.timePeriod))) } .build diff --git a/client/src/main/scala/drt/client/components/TerminalStaffing.scala b/client/src/main/scala/drt/client/components/TerminalStaffing.scala index 663d86193..4d1ca2f34 100644 --- a/client/src/main/scala/drt/client/components/TerminalStaffing.scala +++ b/client/src/main/scala/drt/client/components/TerminalStaffing.scala @@ -211,9 +211,6 @@ object TerminalStaffing { private val component = ScalaComponent.builder[Props]("TerminalStaffing") .renderBackend[Backend] - .componentDidMount { p => - Callback(SetDocumentTitle("Desks and Queues", p.props.terminal, p.props.airportConfig)) - } .build private object MovementDisplay { diff --git a/client/src/main/scala/drt/client/components/TrainingHubComponent.scala b/client/src/main/scala/drt/client/components/TrainingHubComponent.scala index 8951b0fc4..dcc9c758b 100644 --- a/client/src/main/scala/drt/client/components/TrainingHubComponent.scala +++ b/client/src/main/scala/drt/client/components/TrainingHubComponent.scala @@ -67,9 +67,6 @@ object TrainingHubComponent { )) } .configure(Reusability.shouldComponentUpdate) - .componentDidMount { p => - Callback(SetDocumentTitle("Training Hub", p.props.airportConfigPot)) - } .build private def trainingTabs(props: Props): VdomTagOf[UList] = { diff --git a/client/src/main/scala/drt/client/components/scenarios/ScenarioSimulationComponent.scala b/client/src/main/scala/drt/client/components/scenarios/ScenarioSimulationComponent.scala index 19e439a24..12fe2af30 100644 --- a/client/src/main/scala/drt/client/components/scenarios/ScenarioSimulationComponent.scala +++ b/client/src/main/scala/drt/client/components/scenarios/ScenarioSimulationComponent.scala @@ -2,9 +2,7 @@ package drt.client.components.scenarios import diode.UseValueEq import diode.data.Pot -import drt.client.components.SetDocumentTitle import drt.client.components.styles.DefaultFormFieldsStyle -import drt.client.modules.GoogleEventTracker import drt.shared.SimulationResult import io.kinoplan.scalajs.react.material.ui.core._ import japgolly.scalajs.react._ @@ -61,10 +59,6 @@ object ScenarioSimulationComponent extends ScalaCssReactImplicits { State(SimulationFormFields(p.terminal, p.date, p.airportConfig, p.slaConfigs), Map()) ) .renderBackend[Backend] - .componentDidMount { p => - Callback(SetDocumentTitle("Simulate Day", p.props.terminal, p.props.airportConfig)) >> - Callback(GoogleEventTracker.sendPageView(s"Arrival Simulations Page")) - } .build def apply(props: Props): VdomElement = component(props) From 0f8b25de127f11c420abe3d4c3d641915967552c Mon Sep 17 00:00:00 2001 From: Rich Birch Date: Wed, 9 Oct 2024 15:02:43 +0100 Subject: [PATCH 04/12] DRTII-1604 Remove some legacy duplicate page view & event reporting --- .../scala/drt/client/components/DropInDialog.scala | 5 ++++- .../components/FeatureGuideModalComponent.scala | 3 +-- .../components/PortExportDashboardPage.scala | 3 --- .../drt/client/components/TerminalComponent.scala | 4 ---- .../components/TerminalContentComponent.scala | 14 -------------- .../drt/client/components/UserDashboardPage.scala | 3 --- .../ScenarioSimulationFormComponent.scala | 1 - 7 files changed, 5 insertions(+), 28 deletions(-) diff --git a/client/src/main/scala/drt/client/components/DropInDialog.scala b/client/src/main/scala/drt/client/components/DropInDialog.scala index 35b9d1f4a..d8ebfbc38 100644 --- a/client/src/main/scala/drt/client/components/DropInDialog.scala +++ b/client/src/main/scala/drt/client/components/DropInDialog.scala @@ -97,7 +97,10 @@ object DropInDialog extends WithScalaCssImplicits { .builder[Props]("DropDialogComponent") .initialStateFromProps(_ => State(1)) .renderBackend[Backend] - .componentDidMount(_ => Callback(GoogleEventTracker.sendPageView("drop-dialog-component"))) + .componentDidMount { _ => + println(s"DropDialogComponent mounted") + Callback(GoogleEventTracker.sendPageView("Drop-in Dialog")) + } .build def apply(date: String, diff --git a/client/src/main/scala/drt/client/components/FeatureGuideModalComponent.scala b/client/src/main/scala/drt/client/components/FeatureGuideModalComponent.scala index 1d67559d4..44af40c64 100644 --- a/client/src/main/scala/drt/client/components/FeatureGuideModalComponent.scala +++ b/client/src/main/scala/drt/client/components/FeatureGuideModalComponent.scala @@ -107,12 +107,11 @@ object FeatureGuideModalComponent extends WithScalaCssImplicits { .builder[Props]("NavBar") .initialStateFromProps(_ => State(1)) .renderBackend[Backend] - .componentDidMount(_ => Callback(GoogleEventTracker.sendPageView("feature-guide"))) + .componentDidMount(_ => Callback(GoogleEventTracker.sendPageView("Feature guide"))) .build def apply(showDialog: Boolean, closeDialog: ReactEvent => Callback, trainingDataTemplates: Seq[FeatureGuide]): VdomElement = component(Props(showDialog, closeDialog, trainingDataTemplates)) - } diff --git a/client/src/main/scala/drt/client/components/PortExportDashboardPage.scala b/client/src/main/scala/drt/client/components/PortExportDashboardPage.scala index a8e4b161a..08ae83b4a 100644 --- a/client/src/main/scala/drt/client/components/PortExportDashboardPage.scala +++ b/client/src/main/scala/drt/client/components/PortExportDashboardPage.scala @@ -27,9 +27,6 @@ object PortExportDashboardPage { })) }) }) - .componentDidMount(_ => Callback { - GoogleEventTracker.sendPageView(s"dashboard") - }) .build def apply(loggedInUser: LoggedInUser): VdomElement = component(Props(loggedInUser)) diff --git a/client/src/main/scala/drt/client/components/TerminalComponent.scala b/client/src/main/scala/drt/client/components/TerminalComponent.scala index 1c9af176d..f87e0e75e 100644 --- a/client/src/main/scala/drt/client/components/TerminalComponent.scala +++ b/client/src/main/scala/drt/client/components/TerminalComponent.scala @@ -270,7 +270,6 @@ object TerminalComponent { <.a(^.id := "currentTab", "Queues & Arrivals", VdomAttr("data-toggle") := "tab"), ^.onClick ==> { e: ReactEventFromInput => e.preventDefault() - GoogleEventTracker.sendEvent(terminalName, "click", "Queues & Arrivals") props.router.set(props.terminalPageTab.update( mode = Current, subMode = subMode, @@ -281,7 +280,6 @@ object TerminalComponent { <.a(^.id := "planning-tab", VdomAttr("data-toggle") := "tab", "Planning"), ^.onClick ==> { e: ReactEventFromInput => e.preventDefault() - GoogleEventTracker.sendEvent(terminalName, "click", "Planning") props.router.set(props.terminalPageTab.update( mode = Planning, subMode = subMode, @@ -294,7 +292,6 @@ object TerminalComponent { <.a(^.id := "monthlyStaffingTab", ^.className := "flex-forizontally", VdomAttr("data-toggle") := "tab", "Monthly Staffing", " ", monthlyStaffingTooltip), ^.onClick ==> { e: ReactEventFromInput => e.preventDefault() - GoogleEventTracker.sendEvent(terminalName, "click", "Monthly Staffing") props.router.set(props.terminalPageTab.update( mode = Staffing, subMode = "15", @@ -304,7 +301,6 @@ object TerminalComponent { ) else "", <.li(^.className := tabClass(Dashboard), <.a(^.id := "terminalDashboardTab", VdomAttr("data-toggle") := "tab", s"$terminalName Dashboard"), ^.onClick --> { - GoogleEventTracker.sendEvent(terminalName, "click", "Terminal Dashboard") props.router.set( props.terminalPageTab.update( mode = Dashboard, diff --git a/client/src/main/scala/drt/client/components/TerminalContentComponent.scala b/client/src/main/scala/drt/client/components/TerminalContentComponent.scala index 15fc63fbb..94ffed3fe 100644 --- a/client/src/main/scala/drt/client/components/TerminalContentComponent.scala +++ b/client/src/main/scala/drt/client/components/TerminalContentComponent.scala @@ -108,12 +108,10 @@ object TerminalContentComponent { <.ul(^.className := "nav nav-tabs", <.li(^.className := arrivalsActive, <.a(^.id := "arrivalsTab", VdomAttr("data-toggle") := "tab", "Arrivals"), ^.onClick --> { - GoogleEventTracker.sendEvent(terminalName, "Arrivals", props.terminalPageTab.dateFromUrlOrNow.toISODateOnly) props.router.set(props.terminalPageTab.copy(subMode = "arrivals")) }), <.li(^.className := desksAndQueuesActive, <.a(^.className := "flexed-anchor", ^.id := "desksAndQueuesTab", VdomAttr("data-toggle") := "tab", "Desks & Queues"), ^.onClick --> { - GoogleEventTracker.sendEvent(terminalName, "Desks & Queues", props.terminalPageTab.dateFromUrlOrNow.toISODateOnly) props.router.set(props.terminalPageTab.copy( subMode = "desksAndQueues", queryParams = props.terminalPageTab.queryParams.updated("viewType", props.defaultDesksAndQueuesViewType) @@ -122,13 +120,11 @@ object TerminalContentComponent { <.li(^.className := staffingActive, <.a(^.className := "flexed-anchor", ^.id := "staffMovementsTab", VdomAttr("data-toggle") := "tab", "Staff Movements", staffMovementsTabTooltip), ^.onClick --> { - GoogleEventTracker.sendEvent(terminalName, "Staff Movements", props.terminalPageTab.dateFromUrlOrNow.toISODateOnly) props.router.set(props.terminalPageTab.copy(subMode = "staffing")) }), displayForRole( <.li(^.className := simulationsActive, <.a(^.className := "flexed-anchor", ^.id := "simulationDayTab", VdomAttr("data-toggle") := "tab", "Simulate Day"), ^.onClick --> { - GoogleEventTracker.sendEvent(terminalName, "Simulate Day", props.terminalPageTab.dateFromUrlOrNow.toISODateOnly) props.router.set(props.terminalPageTab.copy(subMode = "simulations")) }), ArrivalSimulationUpload, props.loggedInUser @@ -303,16 +299,6 @@ object TerminalContentComponent { val component: Component[Props, State, Backend, CtorType.Props] = ScalaComponent.builder[Props]("TerminalContentComponent") .initialStateFromProps(p => State(p.terminalPageTab.subMode)) .renderBackend[TerminalContentComponent.Backend] - .componentDidMount { p => - Callback { - val hours = p.props.defaultTimeRangeHours - val page = s"${p.props.terminalPageTab.terminal}/${p.props.terminalPageTab.mode}/${p.props.terminalPageTab.subMode}" - val pageWithTime = s"$page/${hours.start}/${hours.end}" - val pageWithDate = p.props.terminalPageTab.maybeViewDate - .map(s => s"$page/$s/${hours.start}/${hours.end}").getOrElse(pageWithTime) - GoogleEventTracker.sendPageView(pageWithDate) - } - } .build def apply(props: Props): VdomElement = component(props) diff --git a/client/src/main/scala/drt/client/components/UserDashboardPage.scala b/client/src/main/scala/drt/client/components/UserDashboardPage.scala index a02ef6c2b..9499a797c 100644 --- a/client/src/main/scala/drt/client/components/UserDashboardPage.scala +++ b/client/src/main/scala/drt/client/components/UserDashboardPage.scala @@ -31,9 +31,6 @@ object UserDashboardPage { } }) - .componentDidMount(_ => Callback { - GoogleEventTracker.sendPageView(s"dashboard") - }) .build def apply(router: RouterCtl[Loc]): VdomElement = component(Props(router)) diff --git a/client/src/main/scala/drt/client/components/scenarios/ScenarioSimulationFormComponent.scala b/client/src/main/scala/drt/client/components/scenarios/ScenarioSimulationFormComponent.scala index 95a3096ab..e65fdb01c 100644 --- a/client/src/main/scala/drt/client/components/scenarios/ScenarioSimulationFormComponent.scala +++ b/client/src/main/scala/drt/client/components/scenarios/ScenarioSimulationFormComponent.scala @@ -302,7 +302,6 @@ object ScenarioSimulationFormComponent extends ScalaCssReactImplicits { ) ) } - .componentDidMount(_ => Callback(GoogleEventTracker.sendPageView(s"Arrival Simulations Page"))) .build private def submitButton(showCharts: Callback, form: SimulationFormFields): WithPropsAndTagsMods = { From a9f8715595ff16443f78483338bda67c07d006ed Mon Sep 17 00:00:00 2001 From: Rich Birch Date: Thu, 10 Oct 2024 09:18:31 +0100 Subject: [PATCH 05/12] DRTII-1604 Manual page view reporting from router --- .../src/main/scala/drt/client/SPAMain.scala | 29 +++++++------ .../drt/client/modules/GoogleAnalytics.scala | 42 ++++++++++--------- server/src/main/twirl/views/index.scala.html | 7 +++- 3 files changed, 44 insertions(+), 34 deletions(-) diff --git a/client/src/main/scala/drt/client/SPAMain.scala b/client/src/main/scala/drt/client/SPAMain.scala index 853cf2646..b6bfe06c2 100644 --- a/client/src/main/scala/drt/client/SPAMain.scala +++ b/client/src/main/scala/drt/client/SPAMain.scala @@ -6,6 +6,7 @@ import drt.client.components.TerminalDesksAndQueues.{ChartsView, Deployments, De import drt.client.components.styles._ import drt.client.components.{FeedsStatusPage, ForecastUploadComponent, GlobalStyles, Layout, PortConfigPage, PortDashboardPage, TerminalComponent, TrainingHubComponent, UserDashboardPage} import drt.client.logger._ +import drt.client.modules.GoogleEventTracker import drt.client.services.JSDateConversions.SDate import drt.client.services._ import drt.client.services.handlers.GetFeedSourceStatuses @@ -108,9 +109,9 @@ object SPAMain { subMode: String = "arrivals", queryParams: Map[String, String] = Map.empty[String, String] ) extends Loc { - val pageName = (modeStr.toLowerCase, subMode.toLowerCase) match { + def pageName = (modeStr.toLowerCase, subMode.toLowerCase) match { case ("current", "arrivals") => "Arrivals" - case ("current", "desksAndQueues") => "Desks and queues" + case ("current", "desksandqueues") => "Desks and queues" case ("current", "staffing") => "Staff movements" case ("current", "simulations") => "Simulate day" case ("planning", _) => "Staff planning" @@ -253,17 +254,19 @@ object SPAMain { loc.title(maybeTerminal) } .onPostRender((maybePrevLoc, currentLoc) => { - Callback( - (maybePrevLoc, currentLoc) match { - case (Some(p: TerminalPageTabLoc), c: TerminalPageTabLoc) => - if (c.updateRequired(p)) SPACircuit.dispatch(c.loadAction) - case (_, c: TerminalPageTabLoc) => - SPACircuit.dispatch(c.loadAction) - case (_, UserDashboardLoc) => - SPACircuit.dispatch(GetUserDashboardState) - case _ => - } - ) + log.info(s"Sending pageview") + Callback(GoogleEventTracker.sendPageView()) >> + Callback( + (maybePrevLoc, currentLoc) match { + case (Some(p: TerminalPageTabLoc), c: TerminalPageTabLoc) => + if (c.updateRequired(p)) SPACircuit.dispatch(c.loadAction) + case (_, c: TerminalPageTabLoc) => + SPACircuit.dispatch(c.loadAction) + case (_, UserDashboardLoc) => + SPACircuit.dispatch(GetUserDashboardState) + case _ => + } + ) }) def homeRoute(dsl: RouterConfigDsl[Loc, Unit]): dsl.Rule = { diff --git a/client/src/main/scala/drt/client/modules/GoogleAnalytics.scala b/client/src/main/scala/drt/client/modules/GoogleAnalytics.scala index 8a63b0753..488c7053c 100644 --- a/client/src/main/scala/drt/client/modules/GoogleAnalytics.scala +++ b/client/src/main/scala/drt/client/modules/GoogleAnalytics.scala @@ -6,6 +6,7 @@ import scala.scalajs.js import scala.scalajs.js.annotation.JSGlobalScope import scala.util.Try import com.dedipresta.crypto.hash.sha256.Sha256 +import org.scalajs.dom.document object GoogleEventTracker { @@ -19,39 +20,42 @@ object GoogleEventTracker { def port: String = dom.document.getElementById("port-code").getAttribute("value") - def userId: String = dom.document.getElementById("user-id").getAttribute("value") +// def userId: String = dom.document.getElementById("user-id").getAttribute("value") - def isScriptLoaded: Boolean = Try(Globals.gtag.isInstanceOf[js.Function]).isSuccess +// def isScriptLoaded: Boolean = Try(Globals.gtag.isInstanceOf[js.Function]).isSuccess - private var hasCreateTrackerRun = false +// private var hasCreateTrackerRun = false - private def runCreateTracker(): Unit = { - if (!hasCreateTrackerRun && userId.nonEmpty && port.nonEmpty && trackingCode.nonEmpty) { - val userUUID = Sha256.hashString(userId) - GoogleAnalytics.gtag("config", trackingCode, js.Dictionary("user_id" -> userUUID, "anonymize_ip" -> true)) - hasCreateTrackerRun = true - } - } +// private def runCreateTracker(): Unit = { +// if (!hasCreateTrackerRun && userId.nonEmpty && port.nonEmpty && trackingCode.nonEmpty) { +// val userUUID = Sha256.hashString(userId) +// GoogleAnalytics.gtag("config", trackingCode, js.Dictionary("user_id" -> userUUID, "anonymize_ip" -> true)) +// hasCreateTrackerRun = true +// } +// } - def sendPageView(page: String): Unit = { - if (isScriptLoaded) { - runCreateTracker() - if (hasCreateTrackerRun) { - GoogleAnalytics.gtag("event", "page_view", js.Dictionary("page" -> s"/$port/$page")) + def sendPageView(additional: String = ""): Unit = { +// if (isScriptLoaded) { +// runCreateTracker() + if (trackingCode.nonEmpty) { + GoogleAnalytics.gtag("event", "page_view", js.Dictionary( + "page_title" -> (document.title + (if (additional.nonEmpty) s" - $additional" else "")), + "page_location" -> document.location.href, + )) } - } +// } } def sendEvent(category: String, action: String, label: String): Unit = { - if (isScriptLoaded && hasCreateTrackerRun) GoogleAnalytics.gtag("event", s"${port}_$category", js.Dictionary("action" -> action, "label" -> label)) + if (trackingCode.nonEmpty) GoogleAnalytics.gtag("event", s"${port}_$category", js.Dictionary("action" -> action, "label" -> label)) } def sendEvent(category: String, action: String, label: String, value: String): Unit = { - if (isScriptLoaded && hasCreateTrackerRun) GoogleAnalytics.gtag("event", s"${port}_$category", js.Dictionary("action" -> action, "label" -> label, "value" -> value)) + if (trackingCode.nonEmpty) GoogleAnalytics.gtag("event", s"${port}_$category", js.Dictionary("action" -> action, "label" -> label, "value" -> value)) } def sendError(description: String, fatal: Boolean): Unit = { - if (isScriptLoaded && hasCreateTrackerRun) GoogleAnalytics.gtag("event", "exception", js.Dictionary("exDescription" -> description, "exFatal" -> fatal)) + if (trackingCode.nonEmpty) GoogleAnalytics.gtag("event", "exception", js.Dictionary("exDescription" -> description, "exFatal" -> fatal)) } } diff --git a/server/src/main/twirl/views/index.scala.html b/server/src/main/twirl/views/index.scala.html index 251ddbd62..071e53576 100644 --- a/server/src/main/twirl/views/index.scala.html +++ b/server/src/main/twirl/views/index.scala.html @@ -16,13 +16,16 @@ } From abb575adbef8c546fa7fb7b998fa4a078bc8cc6c Mon Sep 17 00:00:00 2001 From: Rich Birch Date: Thu, 10 Oct 2024 10:36:33 +0100 Subject: [PATCH 06/12] DRTII-1604 Remove pageviews from componentDidMount hooks --- .../src/main/scala/drt/client/components/DropInDialog.scala | 4 ---- .../drt/client/components/FeatureGuideModalComponent.scala | 1 - .../drt/client/components/TrainingMaterialComponent.scala | 3 --- 3 files changed, 8 deletions(-) diff --git a/client/src/main/scala/drt/client/components/DropInDialog.scala b/client/src/main/scala/drt/client/components/DropInDialog.scala index d8ebfbc38..2acb51a89 100644 --- a/client/src/main/scala/drt/client/components/DropInDialog.scala +++ b/client/src/main/scala/drt/client/components/DropInDialog.scala @@ -97,10 +97,6 @@ object DropInDialog extends WithScalaCssImplicits { .builder[Props]("DropDialogComponent") .initialStateFromProps(_ => State(1)) .renderBackend[Backend] - .componentDidMount { _ => - println(s"DropDialogComponent mounted") - Callback(GoogleEventTracker.sendPageView("Drop-in Dialog")) - } .build def apply(date: String, diff --git a/client/src/main/scala/drt/client/components/FeatureGuideModalComponent.scala b/client/src/main/scala/drt/client/components/FeatureGuideModalComponent.scala index 44af40c64..3254be267 100644 --- a/client/src/main/scala/drt/client/components/FeatureGuideModalComponent.scala +++ b/client/src/main/scala/drt/client/components/FeatureGuideModalComponent.scala @@ -107,7 +107,6 @@ object FeatureGuideModalComponent extends WithScalaCssImplicits { .builder[Props]("NavBar") .initialStateFromProps(_ => State(1)) .renderBackend[Backend] - .componentDidMount(_ => Callback(GoogleEventTracker.sendPageView("Feature guide"))) .build def apply(showDialog: Boolean, diff --git a/client/src/main/scala/drt/client/components/TrainingMaterialComponent.scala b/client/src/main/scala/drt/client/components/TrainingMaterialComponent.scala index 9487bd4f0..bfc311e80 100644 --- a/client/src/main/scala/drt/client/components/TrainingMaterialComponent.scala +++ b/client/src/main/scala/drt/client/components/TrainingMaterialComponent.scala @@ -13,9 +13,6 @@ object TrainingMaterialComponent { .render_P(_ => <.div(^.className := "training-material", <.h3("Training Material"), TrainingMaterialDetailComponent()) ) - .componentDidMount(_ => Callback { - GoogleEventTracker.sendPageView(s"Training Material") - }) .build def apply(): VdomElement = component(Props()) From 007d030cea9e4bb929160efb3333af01c8398500 Mon Sep 17 00:00:00 2001 From: Rich Birch Date: Thu, 10 Oct 2024 10:43:03 +0100 Subject: [PATCH 07/12] DRTII-1604 Debug page titel & url being reported --- .../src/main/scala/drt/client/SPAMain.scala | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/client/src/main/scala/drt/client/SPAMain.scala b/client/src/main/scala/drt/client/SPAMain.scala index b6bfe06c2..ce7dcaf88 100644 --- a/client/src/main/scala/drt/client/SPAMain.scala +++ b/client/src/main/scala/drt/client/SPAMain.scala @@ -244,17 +244,9 @@ object SPAMain { rule.notFound(redirectToPage(PortDashboardLoc(None))(SetRouteVia.HistoryReplace)) } .renderWith(Layout(_, _)) - .setTitle { loc: Loc => - val terminalRegex = """.+terminal/([A-Z0-9]+)/.+""".r - val url = window.location.href - val maybeTerminal = url match { - case terminalRegex(t) => Some(Terminal(t)) - case _ => None - } - loc.title(maybeTerminal) - } + .setTitle(_.title(maybeTerminal)) .onPostRender((maybePrevLoc, currentLoc) => { - log.info(s"Sending pageview") + log.info(s"Sending pageview: ${window.location.href} / ${window.document.title}") Callback(GoogleEventTracker.sendPageView()) >> Callback( (maybePrevLoc, currentLoc) match { @@ -269,6 +261,15 @@ object SPAMain { ) }) + private def maybeTerminal: Option[Terminal] = { + val terminalRegex = """.+terminal/([A-Z0-9]+)/.+""".r + val url = window.location.href + url match { + case terminalRegex(t) => Some(Terminal(t)) + case _ => None + } + } + def homeRoute(dsl: RouterConfigDsl[Loc, Unit]): dsl.Rule = { import dsl._ From 432df3eb2e5bb881aa1b1a63572412ca82d5f9d3 Mon Sep 17 00:00:00 2001 From: Rich Birch Date: Thu, 10 Oct 2024 11:20:38 +0100 Subject: [PATCH 08/12] DRTII-1604 Move urls into router locations --- .../src/main/scala/drt/client/SPAMain.scala | 91 ++++++++++++------- .../drt/client/modules/GoogleAnalytics.scala | 33 +------ 2 files changed, 59 insertions(+), 65 deletions(-) diff --git a/client/src/main/scala/drt/client/SPAMain.scala b/client/src/main/scala/drt/client/SPAMain.scala index ce7dcaf88..d8f04ecc6 100644 --- a/client/src/main/scala/drt/client/SPAMain.scala +++ b/client/src/main/scala/drt/client/SPAMain.scala @@ -30,9 +30,9 @@ import scala.util.Try object SPAMain { sealed trait Loc { + val url: String val portCodeStr = dom.document.getElementById("port-code").getAttribute("value") val portConfig = AirportConfigs.confByPort(PortCode(portCodeStr)) - val url = window.location.href def terminalPart(maybeTerminal: Option[Terminal]): String = { val terminalShortName = maybeTerminal.map { t => @@ -97,6 +97,8 @@ object SPAMain { } object TerminalPageTabLoc { + val hashValue: String = "#terminal" + def apply(terminalName: String, mode: TerminalPageMode, subMode: String, @@ -109,11 +111,14 @@ object SPAMain { subMode: String = "arrivals", queryParams: Map[String, String] = Map.empty[String, String] ) extends Loc { + private val queryString = if (queryParams.nonEmpty) s"?${queryParams.map { case (k, v) => s"$k=$v" }.mkString("&")}" else "" + override val url = s"${TerminalPageTabLoc.hashValue}/$terminalName/$modeStr/$subMode$queryString" def pageName = (modeStr.toLowerCase, subMode.toLowerCase) match { case ("current", "arrivals") => "Arrivals" case ("current", "desksandqueues") => "Desks and queues" case ("current", "staffing") => "Staff movements" case ("current", "simulations") => "Simulate day" + case ("dashboard", "summary") => "Terminal dashboard" case ("planning", _) => "Staff planning" case ("staffing", _) => "Monthly staffing" case _ => "" @@ -182,27 +187,43 @@ object SPAMain { def serverLogEndpoint: String = absoluteUrl("logging") + object PortDashboardLoc { + val hashValue: String = "#portDashboard" + } case class PortDashboardLoc(period: Option[Int]) extends Loc { + override val url = s"${PortDashboardLoc.hashValue}/$period" override def title(maybeTerminal: Option[Terminal]): String = title("Dashboard", maybeTerminal) } case object StatusLoc extends Loc { + val hashValue: String = "#status" + override val url = s"$hashValue" override def title(maybeTerminal: Option[Terminal]): String = title("Feeds status", maybeTerminal) } case object UserDashboardLoc extends Loc { + val hashValue: String = "" + override val url = "" override def title(maybeTerminal: Option[Terminal]): String = title("Dashboard", maybeTerminal) } + object TrainingHubLoc { + val hashValue: String = "#trainingHub" + } case class TrainingHubLoc(modeStr: String = "dropInBooking") extends Loc { + override val url = s"${TrainingHubLoc.hashValue}/$modeStr" override def title(maybeTerminal: Option[Terminal]): String = title("Training hub", maybeTerminal) } case object PortConfigLoc extends Loc { + val hashValue: String = "#config" + override val url = s"$hashValue" override def title(maybeTerminal: Option[Terminal]): String = title("Port config", maybeTerminal) } case object ForecastFileUploadLoc extends Loc { + val hashValue: String = "#forecastFileUpload" + override val url = s"$hashValue" override def title(maybeTerminal: Option[Terminal]): String = title("Forecast upload", maybeTerminal) } @@ -246,8 +267,9 @@ object SPAMain { .renderWith(Layout(_, _)) .setTitle(_.title(maybeTerminal)) .onPostRender((maybePrevLoc, currentLoc) => { - log.info(s"Sending pageview: ${window.location.href} / ${window.document.title}") - Callback(GoogleEventTracker.sendPageView()) >> + val title = currentLoc.title(maybeTerminal) + log.info(s"Sending pageview: $title / ${currentLoc.url}") + Callback(GoogleEventTracker.sendPageView(title, currentLoc.url)) >> Callback( (maybePrevLoc, currentLoc) match { case (Some(p: TerminalPageTabLoc), c: TerminalPageTabLoc) => @@ -276,40 +298,38 @@ object SPAMain { staticRoute(root, UserDashboardLoc) ~> renderR((router: RouterCtl[Loc]) => UserDashboardPage(router)) } - - def statusRoute(dsl: RouterConfigDsl[Loc, Unit]): dsl.Rule = { + def dashboardRoute(dsl: RouterConfigDsl[Loc, Unit]): dsl.Rule = { import dsl._ - val proxy = SPACircuit.connect(m => (m.loggedInUserPot, m.airportConfig)) + val proxy = SPACircuit.connect(_.airportConfig) - staticRoute("#status", StatusLoc) ~> renderR(_ => proxy(p => FeedsStatusPage(p()._1, p()._2))) + dynamicRouteCT((PortDashboardLoc.hashValue / int.option).caseClass[PortDashboardLoc]) ~> + dynRenderR { case (page: PortDashboardLoc, router) => + proxy(p => PortDashboardPage(router, page, p())) + } } - def forecastFileUploadRoute(dsl: RouterConfigDsl[Loc, Unit]): dsl.Rule = { + def terminalRoute(dsl: RouterConfigDsl[Loc, Unit]): dsl.Rule = { import dsl._ - val proxy = SPACircuit.connect(_.airportConfig) - - staticRoute("#forecastFileUpload", ForecastFileUploadLoc) ~> renderR(_ => proxy(ac => ForecastUploadComponent(ac()))) - } + val requiredTerminalName = string("[a-zA-Z0-9]+") + val requiredTopLevelTab = string("[a-zA-Z0-9]+") + val requiredSecondLevelTab = string("[a-zA-Z0-9]+") - def portConfigRoute(dsl: RouterConfigDsl[Loc, Unit]): dsl.Rule = { - import dsl._ - val proxy = SPACircuit.connect(m => - PortConfigPage.Props(m.redListUpdates, m.egateBanksUpdates, m.slaConfigs, m.loggedInUserPot, m.airportConfig, m.gateStandWalkTime) - ) - staticRoute("#config", PortConfigLoc) ~> render(proxy(props => PortConfigPage(props()))) + dynamicRouteCT( + (TerminalPageTabLoc.hashValue / requiredTerminalName / requiredTopLevelTab / requiredSecondLevelTab / "" ~ queryToMap).caseClass[TerminalPageTabLoc]) ~> + dynRenderR { case (page: TerminalPageTabLoc, router) => + val props = TerminalComponent.Props(terminalPageTab = page, router) + ThemeProvider(DrtTheme.theme)(TerminalComponent(props)) + } } - def dashboardRoute(dsl: RouterConfigDsl[Loc, Unit]): dsl.Rule = { + def statusRoute(dsl: RouterConfigDsl[Loc, Unit]): dsl.Rule = { import dsl._ - val proxy = SPACircuit.connect(_.airportConfig) + val proxy = SPACircuit.connect(m => (m.loggedInUserPot, m.airportConfig)) - dynamicRouteCT(("#portDashboard" / int.option).caseClass[PortDashboardLoc]) ~> - dynRenderR { case (page: PortDashboardLoc, router) => - proxy(p => PortDashboardPage(router, page, p())) - } + staticRoute(StatusLoc.hashValue, StatusLoc) ~> renderR(_ => proxy(p => FeedsStatusPage(p()._1, p()._2))) } def trainingHubRoute(dsl: RouterConfigDsl[Loc, Unit]): dsl.Rule = { @@ -318,7 +338,7 @@ object SPAMain { val proxy = SPACircuit.connect(m => (m.loggedInUserPot, m.airportConfig)) dynamicRouteCT( - ("#trainingHub" / string("[a-zA-Z0-9]*")).caseClass[TrainingHubLoc]) ~> + (TrainingHubLoc.hashValue / string("[a-zA-Z0-9]*")).caseClass[TrainingHubLoc]) ~> dynRenderR { case (page: TrainingHubLoc, router) => proxy { p => val props = TrainingHubComponent.Props(trainingHubLoc = page, router, p()._1, p()._2) @@ -327,19 +347,20 @@ object SPAMain { } } - def terminalRoute(dsl: RouterConfigDsl[Loc, Unit]): dsl.Rule = { + def portConfigRoute(dsl: RouterConfigDsl[Loc, Unit]): dsl.Rule = { import dsl._ + val proxy = SPACircuit.connect(m => + PortConfigPage.Props(m.redListUpdates, m.egateBanksUpdates, m.slaConfigs, m.loggedInUserPot, m.airportConfig, m.gateStandWalkTime) + ) + staticRoute(PortDashboardLoc.hashValue, PortConfigLoc) ~> render(proxy(props => PortConfigPage(props()))) + } - val requiredTerminalName = string("[a-zA-Z0-9]+") - val requiredTopLevelTab = string("[a-zA-Z0-9]+") - val requiredSecondLevelTab = string("[a-zA-Z0-9]+") + def forecastFileUploadRoute(dsl: RouterConfigDsl[Loc, Unit]): dsl.Rule = { + import dsl._ - dynamicRouteCT( - ("#terminal" / requiredTerminalName / requiredTopLevelTab / requiredSecondLevelTab / "" ~ queryToMap).caseClass[TerminalPageTabLoc]) ~> - dynRenderR { case (page: TerminalPageTabLoc, router) => - val props = TerminalComponent.Props(terminalPageTab = page, router) - ThemeProvider(DrtTheme.theme)(TerminalComponent(props)) - } + val proxy = SPACircuit.connect(_.airportConfig) + + staticRoute(ForecastFileUploadLoc.hashValue, ForecastFileUploadLoc) ~> renderR(_ => proxy(ac => ForecastUploadComponent(ac()))) } val pathToThisApp: String = dom.document.location.pathname diff --git a/client/src/main/scala/drt/client/modules/GoogleAnalytics.scala b/client/src/main/scala/drt/client/modules/GoogleAnalytics.scala index 488c7053c..169ee16c2 100644 --- a/client/src/main/scala/drt/client/modules/GoogleAnalytics.scala +++ b/client/src/main/scala/drt/client/modules/GoogleAnalytics.scala @@ -4,46 +4,19 @@ import org.scalajs.dom import scala.scalajs.js import scala.scalajs.js.annotation.JSGlobalScope -import scala.util.Try -import com.dedipresta.crypto.hash.sha256.Sha256 -import org.scalajs.dom.document object GoogleEventTracker { - - @js.native - @JSGlobalScope - object Globals extends js.Object { - var gtag: js.Any = js.native - } - def trackingCode: String = dom.document.getElementById("ga-code").getAttribute("value") def port: String = dom.document.getElementById("port-code").getAttribute("value") -// def userId: String = dom.document.getElementById("user-id").getAttribute("value") - -// def isScriptLoaded: Boolean = Try(Globals.gtag.isInstanceOf[js.Function]).isSuccess - -// private var hasCreateTrackerRun = false - -// private def runCreateTracker(): Unit = { -// if (!hasCreateTrackerRun && userId.nonEmpty && port.nonEmpty && trackingCode.nonEmpty) { -// val userUUID = Sha256.hashString(userId) -// GoogleAnalytics.gtag("config", trackingCode, js.Dictionary("user_id" -> userUUID, "anonymize_ip" -> true)) -// hasCreateTrackerRun = true -// } -// } - - def sendPageView(additional: String = ""): Unit = { -// if (isScriptLoaded) { -// runCreateTracker() + def sendPageView(title: String, url: String): Unit = { if (trackingCode.nonEmpty) { GoogleAnalytics.gtag("event", "page_view", js.Dictionary( - "page_title" -> (document.title + (if (additional.nonEmpty) s" - $additional" else "")), - "page_location" -> document.location.href, + "page_title" -> title, + "page_location" -> url, )) } -// } } def sendEvent(category: String, action: String, label: String): Unit = { From 7347ae8155b2ab4a29f0d3a32bc256aa50ffbfd8 Mon Sep 17 00:00:00 2001 From: Rich Birch Date: Thu, 10 Oct 2024 11:40:49 +0100 Subject: [PATCH 09/12] DRTII-1604 Use proper links for tab navigation --- .../src/main/scala/drt/client/SPAMain.scala | 9 ++- .../client/components/TerminalComponent.scala | 55 +++++++------------ .../components/TerminalContentComponent.scala | 43 +++++++++------ .../components/TrainingHubComponent.scala | 29 +++------- 4 files changed, 63 insertions(+), 73 deletions(-) diff --git a/client/src/main/scala/drt/client/SPAMain.scala b/client/src/main/scala/drt/client/SPAMain.scala index d8f04ecc6..c0e523328 100644 --- a/client/src/main/scala/drt/client/SPAMain.scala +++ b/client/src/main/scala/drt/client/SPAMain.scala @@ -212,7 +212,14 @@ object SPAMain { } case class TrainingHubLoc(modeStr: String = "dropInBooking") extends Loc { override val url = s"${TrainingHubLoc.hashValue}/$modeStr" - override def title(maybeTerminal: Option[Terminal]): String = title("Training hub", maybeTerminal) + + val subTtitle = modeStr match { + case "dropInBooking" => "Book a drop-in" + case "trainingMaterial" => "Training material" + case _ => "" + } + + override def title(maybeTerminal: Option[Terminal]): String = title(s"Training hub - $subTtitle", maybeTerminal) } case object PortConfigLoc extends Loc { diff --git a/client/src/main/scala/drt/client/components/TerminalComponent.scala b/client/src/main/scala/drt/client/components/TerminalComponent.scala index f87e0e75e..295f87de8 100644 --- a/client/src/main/scala/drt/client/components/TerminalComponent.scala +++ b/client/src/main/scala/drt/client/components/TerminalComponent.scala @@ -267,47 +267,32 @@ object TerminalComponent { <.ul(^.className := "nav nav-tabs", <.li(^.className := tabClass(Current) + " " + timeMachineClass, - <.a(^.id := "currentTab", "Queues & Arrivals", VdomAttr("data-toggle") := "tab"), - ^.onClick ==> { e: ReactEventFromInput => - e.preventDefault() - props.router.set(props.terminalPageTab.update( - mode = Current, - subMode = subMode, - queryParams = props.terminalPageTab.withUrlParameters(UrlDateParameter(None), viewTypeQueryParam).queryParams - )) - }), + props.router.link(props.terminalPageTab.update( + mode = Current, + subMode = subMode, + queryParams = props.terminalPageTab.withUrlParameters(UrlDateParameter(None), viewTypeQueryParam).queryParams + ))(^.id := "currentTab", "Queues & Arrivals", VdomAttr("data-toggle") := "tab")), <.li(^.className := tabClass(Planning), - <.a(^.id := "planning-tab", VdomAttr("data-toggle") := "tab", "Planning"), - ^.onClick ==> { e: ReactEventFromInput => - e.preventDefault() - props.router.set(props.terminalPageTab.update( - mode = Planning, - subMode = subMode, - queryParams = props.terminalPageTab.withUrlParameters(UrlDateParameter(None), UrlTimeMachineDateParameter(None)).queryParams - )) - } + props.router.link(props.terminalPageTab.update( + mode = Planning, + subMode = subMode, + queryParams = props.terminalPageTab.withUrlParameters(UrlDateParameter(None), UrlTimeMachineDateParameter(None)).queryParams + ))(^.id := "planning-tab", VdomAttr("data-toggle") := "tab", "Planning"), ), if (loggedInUser.roles.contains(StaffEdit)) <.li(^.className := tabClass(Staffing), - <.a(^.id := "monthlyStaffingTab", ^.className := "flex-forizontally", VdomAttr("data-toggle") := "tab", "Monthly Staffing", " ", monthlyStaffingTooltip), - ^.onClick ==> { e: ReactEventFromInput => - e.preventDefault() - props.router.set(props.terminalPageTab.update( - mode = Staffing, - subMode = "15", - queryParams = props.terminalPageTab.withUrlParameters(UrlDateParameter(None), UrlTimeMachineDateParameter(None)).queryParams - )) - } + props.router.link(props.terminalPageTab.update( + mode = Staffing, + subMode = "15", + queryParams = props.terminalPageTab.withUrlParameters(UrlDateParameter(None), UrlTimeMachineDateParameter(None)).queryParams + ))(^.id := "monthlyStaffingTab", ^.className := "flex-forizontally", VdomAttr("data-toggle") := "tab", "Monthly Staffing", " ", monthlyStaffingTooltip) ) else "", <.li(^.className := tabClass(Dashboard), - <.a(^.id := "terminalDashboardTab", VdomAttr("data-toggle") := "tab", s"$terminalName Dashboard"), ^.onClick --> { - props.router.set( - props.terminalPageTab.update( - mode = Dashboard, - subMode = "summary", - queryParams = props.terminalPageTab.withUrlParameters(UrlDateParameter(None), UrlTimeMachineDateParameter(None)).queryParams) - ) - } + props.router.link(props.terminalPageTab.update( + mode = Dashboard, + subMode = "summary", + queryParams = props.terminalPageTab.withUrlParameters(UrlDateParameter(None), UrlTimeMachineDateParameter(None)).queryParams + ))(^.id := "terminalDashboardTab", VdomAttr("data-toggle") := "tab", s"$terminalName Dashboard") ) ) } diff --git a/client/src/main/scala/drt/client/components/TerminalContentComponent.scala b/client/src/main/scala/drt/client/components/TerminalContentComponent.scala index 94ffed3fe..e8b6068ef 100644 --- a/client/src/main/scala/drt/client/components/TerminalContentComponent.scala +++ b/client/src/main/scala/drt/client/components/TerminalContentComponent.scala @@ -107,26 +107,37 @@ object TerminalContentComponent { <.div(^.className := "tabs-with-export", <.ul(^.className := "nav nav-tabs", <.li(^.className := arrivalsActive, - <.a(^.id := "arrivalsTab", VdomAttr("data-toggle") := "tab", "Arrivals"), ^.onClick --> { - props.router.set(props.terminalPageTab.copy(subMode = "arrivals")) - }), + props.router.link(props.terminalPageTab.copy(subMode = "arrivals"))( + ^.id := "arrivalsTab", VdomAttr("data-toggle") := "tab", "Arrivals") + ), <.li(^.className := desksAndQueuesActive, - <.a(^.className := "flexed-anchor", ^.id := "desksAndQueuesTab", VdomAttr("data-toggle") := "tab", "Desks & Queues"), ^.onClick --> { - props.router.set(props.terminalPageTab.copy( - subMode = "desksAndQueues", - queryParams = props.terminalPageTab.queryParams.updated("viewType", props.defaultDesksAndQueuesViewType) - )) - }), + props.router.link(props.terminalPageTab.copy( + subMode = "desksAndQueues", + queryParams = props.terminalPageTab.queryParams.updated("viewType", props.defaultDesksAndQueuesViewType) + ))( + ^.className := "flexed-anchor", + ^.id := "desksAndQueuesTab", + VdomAttr("data-toggle") := "tab", + "Desks & Queues" + ) + ), <.li(^.className := staffingActive, - <.a(^.className := "flexed-anchor", ^.id := "staffMovementsTab", VdomAttr("data-toggle") := "tab", "Staff Movements", staffMovementsTabTooltip), - ^.onClick --> { - props.router.set(props.terminalPageTab.copy(subMode = "staffing")) - }), + props.router.link(props.terminalPageTab.copy(subMode = "staffing"))( + ^.className := "flexed-anchor", + ^.id := "staffMovementsTab", + VdomAttr("data-toggle") := "tab", + "Staff Movements", + staffMovementsTabTooltip) + ), displayForRole( <.li(^.className := simulationsActive, - <.a(^.className := "flexed-anchor", ^.id := "simulationDayTab", VdomAttr("data-toggle") := "tab", "Simulate Day"), ^.onClick --> { - props.router.set(props.terminalPageTab.copy(subMode = "simulations")) - }), + props.router.link(props.terminalPageTab.copy(subMode = "simulations"))( + ^.className := "flexed-anchor", + ^.id := "simulationDayTab", + VdomAttr("data-toggle") := "tab", + "Simulate Day" + ) + ), ArrivalSimulationUpload, props.loggedInUser ) ), diff --git a/client/src/main/scala/drt/client/components/TrainingHubComponent.scala b/client/src/main/scala/drt/client/components/TrainingHubComponent.scala index dcc9c758b..996faf6a0 100644 --- a/client/src/main/scala/drt/client/components/TrainingHubComponent.scala +++ b/client/src/main/scala/drt/client/components/TrainingHubComponent.scala @@ -4,7 +4,6 @@ import diode.data.Pot import diode.{FastEqLowPri, UseValueEq} import drt.client.SPAMain._ import drt.client.logger.{Logger, LoggerFactory} -import drt.client.modules.GoogleEventTracker import drt.client.services._ import drt.client.spa.TrainingHubPageMode import drt.client.spa.TrainingHubPageModes.{DropInBooking, TrainingMaterial} @@ -12,7 +11,7 @@ import drt.shared.{DropIn, DropInRegistration} import japgolly.scalajs.react.component.Scala.Component import japgolly.scalajs.react.extra.router.RouterCtl import japgolly.scalajs.react.vdom.html_<^._ -import japgolly.scalajs.react.{Callback, CtorType, ReactEventFromInput, Reusability, ScalaComponent} +import japgolly.scalajs.react.{CtorType, Reusability, ScalaComponent} import org.scalajs.dom.html.UList import uk.gov.homeoffice.drt.auth.LoggedInUser import uk.gov.homeoffice.drt.ports.AirportConfig @@ -70,33 +69,21 @@ object TrainingHubComponent { .build private def trainingTabs(props: Props): VdomTagOf[UList] = { - val trainingName = props.trainingHubLoc.modeStr - def tabClass(mode: TrainingHubPageMode): String = if (props.trainingHubLoc.modeStr == mode.asString) activeClass else "" <.ul(^.className := "nav nav-tabs", <.li(^.className := tabClass(DropInBooking), - <.a(^.id := "dropInBooking", "Book a Drop-in Session", VdomAttr("data-toggle") := "tab"), - ^.onClick ==> { e: ReactEventFromInput => - e.preventDefault() - GoogleEventTracker.sendEvent(trainingName, "click", "Book a Drop-in Session") - props.router.set(props.trainingHubLoc.copy( - modeStr = DropInBooking.asString - )) - } + props.router.link(props.trainingHubLoc.copy(modeStr = DropInBooking.asString))( + ^.id := "dropInBooking", "Book a Drop-in Session", VdomAttr("data-toggle") := "tab" + ) ), <.li(^.className := tabClass(TrainingMaterial), - <.a(^.id := "trainingMaterial", "Training Material", VdomAttr("data-toggle") := "tab"), - ^.onClick ==> { e: ReactEventFromInput => - e.preventDefault() - GoogleEventTracker.sendEvent(trainingName, "click", "Training Material") - props.router.set(props.trainingHubLoc.copy( - modeStr = TrainingMaterial.asString - )) - }) + props.router.link(props.trainingHubLoc.copy(modeStr = TrainingMaterial.asString))( + ^.id := "trainingMaterial", "Training Material", VdomAttr("data-toggle") := "tab" + ) + ) ) } def apply(props: Props): VdomElement = component(props) - } From ad6ae22f61cde66ee8ef2dc40a60e0093057ab29 Mon Sep 17 00:00:00 2001 From: Rich Birch Date: Thu, 10 Oct 2024 12:33:58 +0100 Subject: [PATCH 10/12] DRTII-1604 Fix e2e tests --- client/src/main/scala/drt/client/SPAMain.scala | 4 ++-- shared/src/main/scala/drt/shared/HasAirportConfig.scala | 9 +++------ 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/client/src/main/scala/drt/client/SPAMain.scala b/client/src/main/scala/drt/client/SPAMain.scala index c0e523328..147dd717e 100644 --- a/client/src/main/scala/drt/client/SPAMain.scala +++ b/client/src/main/scala/drt/client/SPAMain.scala @@ -12,6 +12,7 @@ import drt.client.services._ import drt.client.services.handlers.GetFeedSourceStatuses import drt.client.spa.TerminalPageModes.{Current, Staffing} import drt.client.spa.{TerminalPageMode, TerminalPageModes} +import drt.shared.DrtPortConfigs import io.kinoplan.scalajs.react.material.ui.core.system.ThemeProvider import japgolly.scalajs.react.Callback import japgolly.scalajs.react.extra.router._ @@ -21,7 +22,6 @@ import scalacss.ProdDefaults._ import uk.gov.homeoffice.drt.Urls import uk.gov.homeoffice.drt.ports.PortCode import uk.gov.homeoffice.drt.ports.Terminals.Terminal -import uk.gov.homeoffice.drt.ports.config.AirportConfigs import uk.gov.homeoffice.drt.time.{LocalDate, SDateLike} import scala.scalajs.js.annotation.{JSExport, JSExportTopLevel} @@ -32,7 +32,7 @@ object SPAMain { sealed trait Loc { val url: String val portCodeStr = dom.document.getElementById("port-code").getAttribute("value") - val portConfig = AirportConfigs.confByPort(PortCode(portCodeStr)) + val portConfig = DrtPortConfigs.confByPort(PortCode(portCodeStr)) def terminalPart(maybeTerminal: Option[Terminal]): String = { val terminalShortName = maybeTerminal.map { t => diff --git a/shared/src/main/scala/drt/shared/HasAirportConfig.scala b/shared/src/main/scala/drt/shared/HasAirportConfig.scala index d1ac2172c..643d43e57 100644 --- a/shared/src/main/scala/drt/shared/HasAirportConfig.scala +++ b/shared/src/main/scala/drt/shared/HasAirportConfig.scala @@ -21,13 +21,10 @@ object DrtPortConfigs { import uk.gov.homeoffice.drt.ports.config._ - val testPorts: List[AirportConfigLike] = List(Test, Test2) - val allPorts: List[AirportConfigLike] = AirportConfigs.allPorts ::: testPorts + private val testPorts: List[AirportConfigLike] = List(Test, Test2) + private val allPorts: List[AirportConfigLike] = AirportConfigs.allPorts ::: testPorts - val allPortConfigs: List[AirportConfig] = allPorts.map(_.config) - val testPortConfigs: List[AirportConfig] = testPorts.map(_.config) - - def portGroups: List[String] = allPortConfigs.filterNot(testPorts.contains).map(_.portCode.toString.toUpperCase).sorted + private val allPortConfigs: List[AirportConfig] = allPorts.map(_.config) val confByPort: Map[PortCode, AirportConfig] = allPortConfigs.map(c => (c.portCode, c)).toMap } From 3a827f39e3e54318d095c00a5f14b3a3954c5639 Mon Sep 17 00:00:00 2001 From: Rich Birch Date: Thu, 10 Oct 2024 14:32:17 +0100 Subject: [PATCH 11/12] DRTII-1604 Include domain & protocol in page_location --- client/src/main/scala/drt/client/SPAMain.scala | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/client/src/main/scala/drt/client/SPAMain.scala b/client/src/main/scala/drt/client/SPAMain.scala index 147dd717e..c62e49c98 100644 --- a/client/src/main/scala/drt/client/SPAMain.scala +++ b/client/src/main/scala/drt/client/SPAMain.scala @@ -31,6 +31,10 @@ object SPAMain { sealed trait Loc { val url: String + def href: String = window.location.href.split("#").headOption match { + case Some(head) => head + url + case None => url + } val portCodeStr = dom.document.getElementById("port-code").getAttribute("value") val portConfig = DrtPortConfigs.confByPort(PortCode(portCodeStr)) @@ -275,8 +279,8 @@ object SPAMain { .setTitle(_.title(maybeTerminal)) .onPostRender((maybePrevLoc, currentLoc) => { val title = currentLoc.title(maybeTerminal) - log.info(s"Sending pageview: $title / ${currentLoc.url}") - Callback(GoogleEventTracker.sendPageView(title, currentLoc.url)) >> + log.info(s"Sending pageview: $title (${currentLoc.href})") + Callback(GoogleEventTracker.sendPageView(title, currentLoc.href)) >> Callback( (maybePrevLoc, currentLoc) match { case (Some(p: TerminalPageTabLoc), c: TerminalPageTabLoc) => From 5c69d3b89f0992100d8b78c0145d1fe05b747f9e Mon Sep 17 00:00:00 2001 From: Rich Birch Date: Thu, 10 Oct 2024 14:45:07 +0100 Subject: [PATCH 12/12] DRTII-1604 Fix port dash route --- client/src/main/scala/drt/client/SPAMain.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/main/scala/drt/client/SPAMain.scala b/client/src/main/scala/drt/client/SPAMain.scala index c62e49c98..01172c286 100644 --- a/client/src/main/scala/drt/client/SPAMain.scala +++ b/client/src/main/scala/drt/client/SPAMain.scala @@ -363,7 +363,7 @@ object SPAMain { val proxy = SPACircuit.connect(m => PortConfigPage.Props(m.redListUpdates, m.egateBanksUpdates, m.slaConfigs, m.loggedInUserPot, m.airportConfig, m.gateStandWalkTime) ) - staticRoute(PortDashboardLoc.hashValue, PortConfigLoc) ~> render(proxy(props => PortConfigPage(props()))) + staticRoute(PortConfigLoc.hashValue, PortConfigLoc) ~> render(proxy(props => PortConfigPage(props()))) } def forecastFileUploadRoute(dsl: RouterConfigDsl[Loc, Unit]): dsl.Rule = {