Skip to content

Commit

Permalink
Merge pull request #1937 from UKHomeOffice/DRTII-1604-Duplicate-page-…
Browse files Browse the repository at this point in the history
…views-in-GA

Drtii 1604 duplicate page views in ga
  • Loading branch information
richbirch authored Oct 10, 2024
2 parents dbe1a7e + 5c69d3b commit b807472
Show file tree
Hide file tree
Showing 25 changed files with 214 additions and 478 deletions.
199 changes: 139 additions & 60 deletions client/src/main/scala/drt/client/SPAMain.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,23 @@ 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.modules.GoogleEventTracker
import drt.client.services.JSDateConversions.SDate
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._
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.time.{LocalDate, SDateLike}

Expand All @@ -26,7 +29,28 @@ import scala.util.Try

object SPAMain {

sealed trait Loc
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))

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
Expand Down Expand Up @@ -77,6 +101,8 @@ object SPAMain {
}

object TerminalPageTabLoc {
val hashValue: String = "#terminal"

def apply(terminalName: String,
mode: TerminalPageMode,
subMode: String,
Expand All @@ -89,6 +115,21 @@ 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 _ => ""
}

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(".+"))
Expand Down Expand Up @@ -150,19 +191,52 @@ object SPAMain {

def serverLogEndpoint: String = absoluteUrl("logging")

case class PortDashboardLoc(period: Option[Int]) extends Loc
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 StatusLoc extends Loc
case object UserDashboardLoc extends Loc {
val hashValue: String = ""
override val url = ""
override def title(maybeTerminal: Option[Terminal]): String = title("Dashboard", maybeTerminal)
}

case object UserDashboardLoc extends Loc
object TrainingHubLoc {
val hashValue: String = "#trainingHub"
}
case class TrainingHubLoc(modeStr: String = "dropInBooking") extends Loc {
override val url = s"${TrainingHubLoc.hashValue}/$modeStr"

case object ContactUsLoc extends Loc
val subTtitle = modeStr match {
case "dropInBooking" => "Book a drop-in"
case "trainingMaterial" => "Training material"
case _ => ""
}

case class TrainingHubLoc(modeStr: String = "dropInBooking") extends Loc
override def title(maybeTerminal: Option[Terminal]): String = title(s"Training hub - $subTtitle", maybeTerminal)
}

case object PortConfigLoc extends Loc
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
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)
}

private val initialRequestsActions = Seq(
GetApplicationVersion,
Expand Down Expand Up @@ -195,74 +269,78 @@ object SPAMain {
dashboardRoute(dsl) |
terminalRoute(dsl) |
statusRoute(dsl) |
contactRoute(dsl) |
trainingHubRoute(dsl) |
portConfigRoute(dsl) |
forecastFileUploadRoute(dsl)

rule.notFound(redirectToPage(PortDashboardLoc(None))(SetRouteVia.HistoryReplace))
}
.renderWith(Layout(_, _))
.setTitle(_.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 _ =>
}
)
val title = currentLoc.title(maybeTerminal)
log.info(s"Sending pageview: $title (${currentLoc.href})")
Callback(GoogleEventTracker.sendPageView(title, currentLoc.href)) >>
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 _ =>
}
)
})

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._

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))

staticRoute("#status", StatusLoc) ~> renderR(_ => proxy(p => FeedsStatusPage(p()._1, p()._2)))
}

def contactRoute(dsl: RouterConfigDsl[Loc, Unit]): dsl.Rule = {
import dsl._
val proxy = SPACircuit.connect(_.airportConfig)

staticRoute("#contact", ContactUsLoc) ~> renderR(_ => ContactPage())
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 = {
Expand All @@ -271,7 +349,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)
Expand All @@ -280,19 +358,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(PortConfigLoc.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
Expand Down
Loading

0 comments on commit b807472

Please sign in to comment.