HEX
Server: Microsoft-IIS/10.0
System: Windows NT ITPWINWEBSVR22 10.0 build 20348 (Windows Server 2022) AMD64
User: www.conferencesearch.co.uk (0)
PHP: 8.3.30
Disabled: NONE
Upload Files
File: D:/web/hyperflight/apps/sales-analytics/index-v7.asp
<%
Option Explicit
Response.Buffer = True
Response.CodePage = 65001
Response.CharSet = "utf-8"

' ======================================================================
' HyperFlight Sales Map v13
' Standalone Bootstrap 5 / Classic ASP / MySQL application
' Based on the original inc-template-sales-map.asp report
' ======================================================================

Const adCmdText = 1
Const adParamInput = 1
Const adVarChar = 200

Dim conn, cmd, rs
Dim trendConn, trendCmd, trendRs
Dim countryConn, countryCmd, countryRs
Dim startDate, endDate, requestedStartDate, requestedEndDate, dateRange, dateRangeName
Dim measure, chartView, ukMode, topLimit, previousYears, trendCountry
Dim validationMessage, dataError, rangeLabel
Dim sql, trendSql, countrySql, orderBySql, countryCodeExpr, countryNameExpr, effectiveCountryExpr
Dim dataRows, rowCount, i, tableCount, barCount, pieCount
Dim trendRows, trendRowCount, trendYear, trendMonth
Dim countryRows, countryRowCount, countryListStartDate, countryListEndDate
Dim countryListCode, countryListName, trendCountryFound, trendCountryName
Dim trendComparisonMode, trendSelectedYear, trendCurrentYear, trendCurrentMonth, trendEarliestYear
Dim trendQueryStartDate, trendQueryEndDate, trendSeriesYear, trendSeriesMonth, trendSeriesValue
Dim trendYearIndex, trendHistoricalYear
Dim trendHistoricalValues(10, 12), trendCurrentValues(12)
Dim countryCode, countryName, currentOrders, currentSales, currentValue
Dim totalOrders, totalSales, totalSelected, countryCount
Dim topCountryName, topCountryValue, topCountryShare, averageOrderValue
Dim pieOtherValue, pieDisplayedTotal, shareValue
Dim chartTitle, chartSubtitle, measureLabel, valueColumnLabel

Function IsoDate(ByVal value)
    IsoDate = Year(value) & "-" & Right("0" & Month(value), 2) & "-" & Right("0" & Day(value), 2)
End Function

Function IsValidIsoDate(ByVal value)
    Dim parts, testDate
    IsValidIsoDate = False

    If Len(value) <> 10 Then Exit Function
    parts = Split(value, "-")
    If UBound(parts) <> 2 Then Exit Function
    If Not IsNumeric(parts(0)) Then Exit Function
    If Not IsNumeric(parts(1)) Then Exit Function
    If Not IsNumeric(parts(2)) Then Exit Function

    On Error Resume Next
    testDate = DateSerial(CInt(parts(0)), CInt(parts(1)), CInt(parts(2)))
    If Err.Number = 0 Then
        If Year(testDate) = CInt(parts(0)) And Month(testDate) = CInt(parts(1)) And Day(testDate) = CInt(parts(2)) Then
            IsValidIsoDate = True
        End If
    End If
    Err.Clear
    On Error GoTo 0
End Function

Function ParseIsoDate(ByVal value)
    Dim parts
    parts = Split(value, "-")
    ParseIsoDate = DateSerial(CInt(parts(0)), CInt(parts(1)), CInt(parts(2)))
End Function

Function FriendlyDate(ByVal value)
    FriendlyDate = Day(value) & " " & MonthName(Month(value), True) & " " & Year(value)
End Function

Function Html(ByVal value)
    If IsNull(value) Then
        Html = ""
    Else
        Html = Server.HTMLEncode(CStr(value))
    End If
End Function

Function JsString(ByVal value)
    Dim textValue
    If IsNull(value) Then
        JsString = ""
        Exit Function
    End If

    textValue = CStr(value)
    textValue = Replace(textValue, "\", "\\")
    textValue = Replace(textValue, Chr(34), "\" & Chr(34))
    textValue = Replace(textValue, "'", "\'")
    textValue = Replace(textValue, vbCr, "\r")
    textValue = Replace(textValue, vbLf, "\n")
    textValue = Replace(textValue, "</", "<\/")
    JsString = textValue
End Function

Function JsNumber(ByVal value)
    Dim textValue
    If IsNull(value) Or IsEmpty(value) Then
        JsNumber = "0"
    Else
        textValue = CStr(CDbl(value))
        textValue = Replace(textValue, ",", ".")
        JsNumber = textValue
    End If
End Function

Function Number0(ByVal value)
    If IsNull(value) Or IsEmpty(value) Then
        Number0 = "0"
    Else
        Number0 = FormatNumber(CDbl(value), 0)
    End If
End Function

Function Money0(ByVal value)
    If IsNull(value) Or IsEmpty(value) Then
        Money0 = "&pound;0"
    Else
        Money0 = "&pound;" & FormatNumber(CDbl(value), 0)
    End If
End Function

Function BuildViewUrl(ByVal requestedView)
    BuildViewUrl = "?DateRange=" & Server.URLEncode(dateRange) & _
                   "&StartDate=" & Server.URLEncode(startDate) & _
                   "&EndDate=" & Server.URLEncode(endDate) & _
                   "&Measure=" & Server.URLEncode(measure) & _
                   "&UK=" & Server.URLEncode(ukMode) & _
                   "&TopLimit=" & topLimit & _
                   "&PreviousYears=" & previousYears & _
                   "&TrendCountry=" & Server.URLEncode(trendCountry) & _
                   "&View=" & Server.URLEncode(requestedView)
End Function

' ----------------------------------------------------------------------
' Filters and defaults
' ----------------------------------------------------------------------
requestedStartDate = Trim(Request.QueryString("StartDate"))
requestedEndDate = Trim(Request.QueryString("EndDate"))
dateRange = LCase(Trim(Request.QueryString("DateRange")))
measure = LCase(Trim(Request.QueryString("Measure")))
chartView = LCase(Trim(Request.QueryString("View")))
ukMode = LCase(Trim(Request.QueryString("UK")))
topLimit = Trim(Request.QueryString("TopLimit"))
previousYears = Trim(Request.QueryString("PreviousYears"))
trendCountry = Trim(Request.QueryString("TrendCountry"))

' Existing bookmarked URLs containing dates but no DateRange remain valid.
If dateRange = "" Then
    If requestedStartDate <> "" Or requestedEndDate <> "" Then
        dateRange = "custom"
    Else
        dateRange = "12months"
    End If
End If

endDate = IsoDate(Date())
Select Case dateRange
    Case "30days"
        dateRangeName = "Past 30 days"
        startDate = IsoDate(DateAdd("d", -29, Date()))
    Case "60days"
        dateRangeName = "Past 60 days"
        startDate = IsoDate(DateAdd("d", -59, Date()))
    Case "90days"
        dateRangeName = "Past 90 days"
        startDate = IsoDate(DateAdd("d", -89, Date()))
    Case "thisquarter"
        dateRangeName = "This quarter"
        startDate = IsoDate(DateSerial(Year(Date()), ((Month(Date()) - 1) \ 3) * 3 + 1, 1))
    Case "previousquarter"
        dateRangeName = "Previous quarter"
        startDate = IsoDate(DateAdd("m", -3, DateSerial(Year(Date()), ((Month(Date()) - 1) \ 3) * 3 + 1, 1)))
        endDate = IsoDate(DateAdd("d", -1, DateSerial(Year(Date()), ((Month(Date()) - 1) \ 3) * 3 + 1, 1)))
    Case "6months"
        dateRangeName = "Past 6 months"
        startDate = IsoDate(DateAdd("m", -6, Date()))
    Case "12months"
        dateRangeName = "Past 12 months"
        startDate = IsoDate(DateAdd("m", -12, Date()))
    Case "yeartodate"
        dateRangeName = "Year to date"
        startDate = IsoDate(DateSerial(Year(Date()), 1, 1))
    Case "lastyear"
        dateRangeName = "Last calendar year"
        startDate = IsoDate(DateSerial(Year(Date()) - 1, 1, 1))
        endDate = IsoDate(DateSerial(Year(Date()) - 1, 12, 31))
    Case "2years"
        dateRangeName = "Past 2 years"
        startDate = IsoDate(DateAdd("yyyy", -2, Date()))
    Case "3years"
        dateRangeName = "Past 3 years"
        startDate = IsoDate(DateAdd("yyyy", -3, Date()))
    Case "5years"
        dateRangeName = "Past 5 years"
        startDate = IsoDate(DateAdd("yyyy", -5, Date()))
    Case "all"
        dateRangeName = "All time"
        startDate = ""
    Case "custom"
        dateRangeName = "Custom dates"
        startDate = requestedStartDate
        endDate = requestedEndDate
    Case Else
        dateRange = "12months"
        dateRangeName = "Past 12 months"
        startDate = IsoDate(DateAdd("m", -12, Date()))
End Select

If measure = "" Then measure = "sales"
If chartView = "" Then chartView = "bar"
If ukMode = "" Then ukMode = "include"
If topLimit = "" Or Not IsNumeric(topLimit) Then topLimit = 15

topLimit = CInt(topLimit)
If topLimit <> 10 And topLimit <> 15 And topLimit <> 20 And topLimit <> 30 Then topLimit = 15

If previousYears = "" Or Not IsNumeric(previousYears) Then previousYears = 1
previousYears = CInt(previousYears)
If previousYears < 1 Or previousYears > 10 Then previousYears = 1

If trendCountry = "" Or LCase(trendCountry) = "all" Then
    trendCountry = "all"
Else
    trendCountry = UCase(trendCountry)
End If

Select Case measure
    Case "sales", "orders"
        ' Valid.
    Case Else
        measure = "sales"
End Select

Select Case chartView
    Case "world", "europe", "bar", "pie", "trend"
        ' Valid.
    Case Else
        chartView = "bar"
End Select

Select Case ukMode
    Case "include", "exclude"
        ' Valid.
    Case Else
        ukMode = "include"
End Select

validationMessage = ""
If dateRange = "all" Then
    If Not IsValidIsoDate(endDate) Then validationMessage = "Please enter a valid end date."
ElseIf Not IsValidIsoDate(startDate) Or Not IsValidIsoDate(endDate) Then
    validationMessage = "Please enter valid start and end dates."
ElseIf ParseIsoDate(startDate) > ParseIsoDate(endDate) Then
    validationMessage = "The start date must not be later than the end date."
End If

If validationMessage = "" Then
    If dateRange = "all" Then
        rangeLabel = Html(dateRangeName)
    Else
        rangeLabel = Html(dateRangeName) & " &#183; " & _
                     Html(FriendlyDate(ParseIsoDate(startDate))) & " to " & _
                     Html(FriendlyDate(ParseIsoDate(endDate)))
    End If
Else
    rangeLabel = Html(dateRangeName)
End If

If measure = "orders" Then
    orderBySql = "OrderCount"
    measureLabel = "Orders"
    valueColumnLabel = "Orders"
Else
    orderBySql = "SalesTotal"
    measureLabel = "Sales value"
    valueColumnLabel = "Sales"
End If

Select Case chartView
    Case "europe"
        chartTitle = "Europe map"
    Case "bar"
        chartTitle = "Top countries"
    Case "pie"
        chartTitle = "Country share"
    Case "trend"
        If measure = "orders" Then
            chartTitle = "Monthly order trend"
        Else
            chartTitle = "Monthly sales trend"
        End If
    Case Else
        chartTitle = "World map"
End Select

trendComparisonMode = False
If chartView = "trend" And dateRange = "lastyear" Then
    trendComparisonMode = True
    trendCurrentYear = Year(Date())
    trendSelectedYear = trendCurrentYear - 1
    trendEarliestYear = trendSelectedYear - previousYears
    trendCurrentMonth = Month(Date())
End If

If chartView = "trend" Then
    If trendComparisonMode Then
        If previousYears = 1 Then
            chartSubtitle = measureLabel & " by month: " & trendEarliestYear & ", " & trendSelectedYear & " and " & trendCurrentYear & " year to date"
        Else
            chartSubtitle = measureLabel & " by month: " & trendEarliestYear & " to " & trendSelectedYear & " and " & trendCurrentYear & " year to date"
        End If
    Else
        chartSubtitle = measureLabel & " by month"
    End If
Else
    chartSubtitle = measureLabel & " by country"
End If

' ----------------------------------------------------------------------
' Query and aggregate data
' ----------------------------------------------------------------------
rowCount = 0
totalOrders = 0
totalSales = 0
totalSelected = 0
countryCount = 0
topCountryName = ""
topCountryValue = 0
topCountryShare = 0
averageOrderValue = 0
pieOtherValue = 0
trendRowCount = 0
countryRowCount = 0
trendCountryFound = False
trendCountryName = "All countries"
dataError = ""

If validationMessage = "" Then
    effectiveCountryExpr = "COALESCE(NULLIF(TRIM(o.DeliveryCountry), ''), TRIM(o.Country))"
    countryCodeExpr = "CASE WHEN LEFT(c.Country, 14) = 'United Kingdom' THEN 'GB' ELSE UPPER(c.CodeA2) END"
    countryNameExpr = "CASE WHEN LEFT(c.Country, 14) = 'United Kingdom' THEN 'United Kingdom' ELSE c.Country END"

    sql = "SELECT " & countryCodeExpr & " AS CountryCode, " & _
          countryNameExpr & " AS CountryName, " & _
          "COUNT(*) AS OrderCount, " & _
          "SUM(IFNULL(o.GrandTotal, 0) - IFNULL(o.VATIncluded, 0)) AS SalesTotal " & _
          "FROM orders o " & _
          "INNER JOIN countries c ON c.Country = " & effectiveCountryExpr & " " & _
          "WHERE o.IsInvoice = TRUE " & _
          "AND o.PaymentReceived = TRUE " & _
          "AND o.Status <> 'CANCELLED' " & _
          "AND o.Status <> 'ORDER PLACED' "

    If dateRange <> "all" Then
        sql = sql & "AND o.DateTimePaid >= ? "
    End If

    sql = sql & "AND o.DateTimePaid < DATE_ADD(?, INTERVAL 1 DAY) " & _
                "AND TRIM(IFNULL(c.CodeA2, '')) <> '' "

    If ukMode = "exclude" Then
        sql = sql & "AND LEFT(c.Country, 14) <> 'United Kingdom' "
    End If

    sql = sql & "GROUP BY " & countryCodeExpr & ", " & countryNameExpr & " " & _
                "ORDER BY " & orderBySql & " DESC, CountryName ASC"

    On Error Resume Next

    Set conn = Server.CreateObject("ADODB.Connection")
    conn.Open "DSN=MySQL_hyperflight;"

    If Err.Number = 0 Then
        Set cmd = Server.CreateObject("ADODB.Command")
        Set cmd.ActiveConnection = conn
        cmd.CommandType = adCmdText
        cmd.CommandText = sql
        If dateRange <> "all" Then
            cmd.Parameters.Append cmd.CreateParameter("@DateFrom", adVarChar, adParamInput, 10, startDate)
        End If
        cmd.Parameters.Append cmd.CreateParameter("@DateTo", adVarChar, adParamInput, 10, endDate)
        Set rs = cmd.Execute()
    End If

    If Err.Number <> 0 Then
        dataError = Err.Description
        Err.Clear
    ElseIf Not rs.EOF Then
        dataRows = rs.GetRows()
        rowCount = UBound(dataRows, 2) + 1
    End If

    If IsObject(rs) Then
        If rs.State <> 0 Then rs.Close
        Set rs = Nothing
    End If
    Set cmd = Nothing
    If IsObject(conn) Then
        If conn.State <> 0 Then conn.Close
        Set conn = Nothing
    End If

    On Error GoTo 0

    If dataError = "" And rowCount > 0 Then
        For i = 0 To rowCount - 1
            currentOrders = 0
            currentSales = 0
            If Not IsNull(dataRows(2, i)) Then currentOrders = CLng(dataRows(2, i))
            If Not IsNull(dataRows(3, i)) Then currentSales = CDbl(dataRows(3, i))

            totalOrders = totalOrders + currentOrders
            totalSales = totalSales + currentSales
        Next

        If measure = "orders" Then
            totalSelected = CDbl(totalOrders)
        Else
            totalSelected = totalSales
        End If

        countryCount = rowCount
        topCountryName = CStr(dataRows(1, 0))
        If measure = "orders" Then
            topCountryValue = CDbl(dataRows(2, 0))
        Else
            topCountryValue = CDbl(dataRows(3, 0))
        End If

        If totalSelected > 0 Then topCountryShare = (topCountryValue / totalSelected) * 100
        If totalOrders > 0 Then averageOrderValue = totalSales / totalOrders
    End If
End If

' Load countries available to the Sales trend filter.
If validationMessage = "" And dataError = "" And rowCount > 0 And chartView = "trend" Then
    countryListStartDate = startDate
    countryListEndDate = endDate

    If trendComparisonMode Then
        countryListStartDate = IsoDate(DateSerial(trendEarliestYear, 1, 1))
        countryListEndDate = IsoDate(Date())
    End If

    countrySql = "SELECT " & countryCodeExpr & " AS CountryCode, " & _
                 countryNameExpr & " AS CountryName " & _
                 "FROM orders o " & _
                 "INNER JOIN countries c ON c.Country = " & effectiveCountryExpr & " " & _
                 "WHERE o.IsInvoice = TRUE " & _
                 "AND o.PaymentReceived = TRUE " & _
                 "AND o.Status <> 'CANCELLED' " & _
                 "AND o.Status <> 'ORDER PLACED' "

    If countryListStartDate <> "" Then
        countrySql = countrySql & "AND o.DateTimePaid >= ? "
    End If

    countrySql = countrySql & "AND o.DateTimePaid < DATE_ADD(?, INTERVAL 1 DAY) " & _
                              "AND TRIM(IFNULL(c.CodeA2, '')) <> '' "

    If ukMode = "exclude" Then
        countrySql = countrySql & "AND LEFT(c.Country, 14) <> 'United Kingdom' "
    End If

    countrySql = countrySql & "GROUP BY " & countryCodeExpr & ", " & countryNameExpr & " " & _
                              "ORDER BY CountryName ASC"

    On Error Resume Next

    Set countryConn = Server.CreateObject("ADODB.Connection")
    countryConn.Open "DSN=MySQL_hyperflight;"

    If Err.Number = 0 Then
        Set countryCmd = Server.CreateObject("ADODB.Command")
        Set countryCmd.ActiveConnection = countryConn
        countryCmd.CommandType = adCmdText
        countryCmd.CommandText = countrySql
        If countryListStartDate <> "" Then
            countryCmd.Parameters.Append countryCmd.CreateParameter("@CountryDateFrom", adVarChar, adParamInput, 10, countryListStartDate)
        End If
        countryCmd.Parameters.Append countryCmd.CreateParameter("@CountryDateTo", adVarChar, adParamInput, 10, countryListEndDate)
        Set countryRs = countryCmd.Execute()
    End If

    If Err.Number <> 0 Then
        dataError = Err.Description
        Err.Clear
    ElseIf Not countryRs.EOF Then
        countryRows = countryRs.GetRows()
        countryRowCount = UBound(countryRows, 2) + 1
    End If

    If IsObject(countryRs) Then
        If countryRs.State <> 0 Then countryRs.Close
        Set countryRs = Nothing
    End If
    Set countryCmd = Nothing
    If IsObject(countryConn) Then
        If countryConn.State <> 0 Then countryConn.Close
        Set countryConn = Nothing
    End If

    On Error GoTo 0

    If dataError = "" Then
        If trendCountry = "all" Then
            trendCountryFound = True
        Else
            For i = 0 To countryRowCount - 1
                countryListCode = UCase(CStr(countryRows(0, i)))
                If countryListCode = trendCountry Then
                    trendCountryFound = True
                    trendCountryName = CStr(countryRows(1, i))
                    Exit For
                End If
            Next
        End If

        If Not trendCountryFound Then
            trendCountry = "all"
            trendCountryName = "All countries"
        End If

        If trendCountry <> "all" Then
            chartSubtitle = chartSubtitle & " (" & trendCountryName & ")"
        End If
    End If
End If

' Load monthly totals only when the trend view is requested.
If validationMessage = "" And dataError = "" And rowCount > 0 And chartView = "trend" Then
    trendQueryStartDate = startDate
    trendQueryEndDate = endDate

    If trendComparisonMode Then
        trendQueryStartDate = IsoDate(DateSerial(trendEarliestYear, 1, 1))
        trendQueryEndDate = IsoDate(Date())
    End If

    trendSql = "SELECT YEAR(o.DateTimePaid) AS SalesYear, " & _
               "MONTH(o.DateTimePaid) AS SalesMonth, " & _
               "COUNT(*) AS OrderCount, " & _
               "SUM(IFNULL(o.GrandTotal, 0) - IFNULL(o.VATIncluded, 0)) AS SalesTotal " & _
               "FROM orders o " & _
               "INNER JOIN countries c ON c.Country = " & effectiveCountryExpr & " " & _
               "WHERE o.IsInvoice = TRUE " & _
               "AND o.PaymentReceived = TRUE " & _
               "AND o.Status <> 'CANCELLED' " & _
               "AND o.Status <> 'ORDER PLACED' "

    If dateRange <> "all" Then
        trendSql = trendSql & "AND o.DateTimePaid >= ? "
    End If

    trendSql = trendSql & "AND o.DateTimePaid < DATE_ADD(?, INTERVAL 1 DAY) " & _
                          "AND TRIM(IFNULL(c.CodeA2, '')) <> '' "

    If ukMode = "exclude" Then
        trendSql = trendSql & "AND LEFT(c.Country, 14) <> 'United Kingdom' "
    End If

    If trendCountry <> "all" Then
        trendSql = trendSql & "AND " & countryCodeExpr & " = ? "
    End If

    trendSql = trendSql & "GROUP BY YEAR(o.DateTimePaid), MONTH(o.DateTimePaid) " & _
                          "ORDER BY SalesYear ASC, SalesMonth ASC"

    On Error Resume Next

    Set trendConn = Server.CreateObject("ADODB.Connection")
    trendConn.Open "DSN=MySQL_hyperflight;"

    If Err.Number = 0 Then
        Set trendCmd = Server.CreateObject("ADODB.Command")
        Set trendCmd.ActiveConnection = trendConn
        trendCmd.CommandType = adCmdText
        trendCmd.CommandText = trendSql
        If dateRange <> "all" Then
            trendCmd.Parameters.Append trendCmd.CreateParameter("@TrendDateFrom", adVarChar, adParamInput, 10, trendQueryStartDate)
        End If
        trendCmd.Parameters.Append trendCmd.CreateParameter("@TrendDateTo", adVarChar, adParamInput, 10, trendQueryEndDate)
        If trendCountry <> "all" Then
            trendCmd.Parameters.Append trendCmd.CreateParameter("@TrendCountry", adVarChar, adParamInput, 10, trendCountry)
        End If
        Set trendRs = trendCmd.Execute()
    End If

    If Err.Number <> 0 Then
        dataError = Err.Description
        Err.Clear
    ElseIf Not trendRs.EOF Then
        trendRows = trendRs.GetRows()
        trendRowCount = UBound(trendRows, 2) + 1
    End If

    If IsObject(trendRs) Then
        If trendRs.State <> 0 Then trendRs.Close
        Set trendRs = Nothing
    End If
    Set trendCmd = Nothing
    If IsObject(trendConn) Then
        If trendConn.State <> 0 Then trendConn.Close
        Set trendConn = Nothing
    End If

    On Error GoTo 0

    If trendComparisonMode And trendRowCount > 0 Then
        For i = 0 To trendRowCount - 1
            trendSeriesYear = CInt(trendRows(0, i))
            trendSeriesMonth = CInt(trendRows(1, i))

            If measure = "orders" Then
                trendSeriesValue = CDbl(trendRows(2, i))
            Else
                trendSeriesValue = CDbl(trendRows(3, i))
            End If

            If trendSeriesYear >= trendEarliestYear And trendSeriesYear <= trendSelectedYear Then
                trendYearIndex = trendSeriesYear - trendEarliestYear
                trendHistoricalValues(trendYearIndex, trendSeriesMonth) = trendSeriesValue
            ElseIf trendSeriesYear = trendCurrentYear Then
                trendCurrentValues(trendSeriesMonth) = trendSeriesValue
            End If
        Next
    End If
End If

tableCount = rowCount
If tableCount > topLimit Then tableCount = topLimit
barCount = tableCount
pieCount = rowCount
If pieCount > 10 Then pieCount = 10
If pieCount > topLimit Then pieCount = topLimit

pieDisplayedTotal = 0
If rowCount > 0 Then
    For i = 0 To pieCount - 1
        If measure = "orders" Then
            pieDisplayedTotal = pieDisplayedTotal + CDbl(dataRows(2, i))
        Else
            pieDisplayedTotal = pieDisplayedTotal + CDbl(dataRows(3, i))
        End If
    Next
    pieOtherValue = totalSelected - pieDisplayedTotal
    If pieOtherValue < 0.000001 Then pieOtherValue = 0
End If
%>
<!doctype html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>HyperFlight Sales Map</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-sRIl4kxILFvY47J16cr9ZwB07vP4J8+LH7qKQnuqkuIAvNWLzeN8tE5YBujZqJLB" crossorigin="anonymous">
    <% If validationMessage = "" And dataError = "" And rowCount > 0 Then %>
    <script src="https://www.gstatic.com/charts/loader.js"></script>
    <% End If %>
    <style>
        body { background: #f5f6f8; }
        .report-wrap { max-width: 1500px; }
        .metric-card { min-height: 118px; }
        .metric-value { font-size: 1.75rem; font-weight: 700; line-height: 1.15; }
        .metric-value.metric-text { font-size: 1.25rem; }
        .metric-label { color: #6c757d; font-size: 0.9rem; }
        .card-header h2 { font-size: 1.05rem; margin: 0; }
        .chart-shell { position: relative; min-height: 500px; }
        #sales-chart { width: 100%; min-height: 500px; }
        .chart-loading {
            position: absolute;
            inset: 0;
            z-index: 2;
            display: flex;
            align-items: center;
            justify-content: center;
            background: rgba(255, 255, 255, 0.88);
        }
        .chart-header-title { min-width: 0; flex: 1 1 auto; }
        .chart-header-actions { flex: 0 0 auto; }
        .view-nav { flex-wrap: nowrap !important; }
        .view-nav .btn { min-width: 100px; white-space: nowrap; }
        .trend-filters-form { flex-wrap: nowrap !important; }
        .trend-filters-form .previous-years-select { width: 72px; min-width: 72px; }
        .trend-filters-form .country-select { width: 180px; min-width: 180px; max-width: 180px; }
        .country-name { min-width: 190px; }
        .table th { white-space: nowrap; }
        .small-note { font-size: 0.875rem; color: #6c757d; }
        @media (max-width: 1399.98px) {
            .chart-header-actions { flex-wrap: wrap; }
            .trend-filters-form { flex-wrap: wrap !important; }
            .view-nav { flex-wrap: wrap !important; }
        }
        @media (max-width: 767.98px) {
            .chart-shell, #sales-chart { min-height: 420px; }
            .view-nav .btn { min-width: 0; }
            .trend-filters-form .country-select { width: 100%; min-width: 180px; max-width: none; }
        }
    </style>
</head>
<body class="pb-5">
<div class="container-fluid report-wrap py-4 px-3 px-lg-4">

    <div class="card shadow-sm mb-4">
        <div class="card-header bg-primary text-white py-3">
            <div class="d-flex flex-column flex-lg-row justify-content-between gap-2 align-items-lg-center">
                <div>
                    <h1 class="h4 mb-1">HyperFlight Sales Map</h1>
                    <div class="small opacity-75">Geographical distribution of orders and sales</div>
                </div>
                <span class="badge text-bg-light"><%=rangeLabel%></span>
            </div>
        </div>
        <div class="card-body">
            <form method="get" action="" class="row g-3 align-items-end">
                <div class="col-sm-6 col-lg-2">
                    <label for="DateRange" class="form-label fw-semibold">Date range</label>
                    <select class="form-select" id="DateRange" name="DateRange">
                        <option value="30days" <% If dateRange = "30days" Then Response.Write "selected" %>>Past 30 days</option>
                        <option value="60days" <% If dateRange = "60days" Then Response.Write "selected" %>>Past 60 days</option>
                        <option value="90days" <% If dateRange = "90days" Then Response.Write "selected" %>>Past 90 days</option>
                        <option value="thisquarter" <% If dateRange = "thisquarter" Then Response.Write "selected" %>>This quarter</option>
                        <option value="previousquarter" <% If dateRange = "previousquarter" Then Response.Write "selected" %>>Previous quarter</option>
                        <option value="6months" <% If dateRange = "6months" Then Response.Write "selected" %>>Past 6 months</option>
                        <option value="12months" <% If dateRange = "12months" Then Response.Write "selected" %>>Past 12 months</option>
                        <option value="yeartodate" <% If dateRange = "yeartodate" Then Response.Write "selected" %>>Year to date</option>
                        <option value="lastyear" <% If dateRange = "lastyear" Then Response.Write "selected" %>>Last calendar year</option>
                        <option value="2years" <% If dateRange = "2years" Then Response.Write "selected" %>>Past 2 years</option>
                        <option value="3years" <% If dateRange = "3years" Then Response.Write "selected" %>>Past 3 years</option>
                        <option value="5years" <% If dateRange = "5years" Then Response.Write "selected" %>>Past 5 years</option>
                        <option value="all" <% If dateRange = "all" Then Response.Write "selected" %>>All time</option>
                        <option value="custom" <% If dateRange = "custom" Then Response.Write "selected" %>>Custom dates</option>
                    </select>
                </div>
                <div class="col-sm-6 col-lg-2">
                    <label for="StartDate" class="form-label fw-semibold">Start date</label>
                    <input type="date" class="form-control" id="StartDate" name="StartDate" value="<%=Html(startDate)%>"<% If dateRange = "custom" Then Response.Write " required" Else Response.Write " readonly" %>>
                </div>
                <div class="col-sm-6 col-lg-2">
                    <label for="EndDate" class="form-label fw-semibold">End date</label>
                    <input type="date" class="form-control" id="EndDate" name="EndDate" value="<%=Html(endDate)%>"<% If dateRange = "custom" Then Response.Write " required" Else Response.Write " readonly" %>>
                </div>
                <div class="col-sm-6 col-lg-2">
                    <label for="Measure" class="form-label fw-semibold">Measure</label>
                    <select class="form-select" id="Measure" name="Measure">
                        <option value="sales" <% If measure = "sales" Then Response.Write "selected" %>>Sales value</option>
                        <option value="orders" <% If measure = "orders" Then Response.Write "selected" %>>Number of orders</option>
                    </select>
                </div>
                <div class="col-sm-6 col-lg-2">
                    <label for="UK" class="form-label fw-semibold">United Kingdom</label>
                    <select class="form-select" id="UK" name="UK">
                        <option value="include" <% If ukMode = "include" Then Response.Write "selected" %>>Include</option>
                        <option value="exclude" <% If ukMode = "exclude" Then Response.Write "selected" %>>Exclude</option>
                    </select>
                </div>
                <div class="col-sm-3 col-lg-1">
                    <label for="TopLimit" class="form-label fw-semibold">Top</label>
                    <select class="form-select" id="TopLimit" name="TopLimit">
                        <option value="10" <% If topLimit = 10 Then Response.Write "selected" %>>10</option>
                        <option value="15" <% If topLimit = 15 Then Response.Write "selected" %>>15</option>
                        <option value="20" <% If topLimit = 20 Then Response.Write "selected" %>>20</option>
                        <option value="30" <% If topLimit = 30 Then Response.Write "selected" %>>30</option>
                    </select>
                </div>
                <div class="col-sm-3 col-lg-1 d-grid">
                    <input type="hidden" name="PreviousYears" value="<%=previousYears%>">
                    <input type="hidden" name="TrendCountry" value="<%=Html(trendCountry)%>">
                    <input type="hidden" name="View" value="<%=Html(chartView)%>">
                    <button type="submit" class="btn btn-primary fw-semibold">Run</button>
                </div>
            </form>
            <div class="small-note mt-3">Sales values exclude VAT. Countries are based on the delivery address, falling back to the billing address where no delivery country is recorded. Partial refunds are not taken into account.</div>
        </div>
    </div>

    <% If validationMessage <> "" Then %>
        <div class="alert alert-warning shadow-sm"><%=Html(validationMessage)%></div>
    <% ElseIf dataError <> "" Then %>
        <div class="alert alert-danger shadow-sm">
            <strong>The report could not be loaded.</strong>
            <div class="small mt-1"><%=Html(dataError)%></div>
        </div>
    <% ElseIf rowCount = 0 Then %>
        <div class="alert alert-info shadow-sm">No sales were found for the selected filters.</div>
    <% Else %>

        <div class="row g-3 mb-4">
            <div class="col-6 col-lg-4 col-xxl-2">
                <div class="card shadow-sm h-100 metric-card"><div class="card-body">
                    <div class="metric-value"><%=Money0(totalSales)%></div>
                    <div class="metric-label">Sales total</div>
                </div></div>
            </div>
            <div class="col-6 col-lg-4 col-xxl-2">
                <div class="card shadow-sm h-100 metric-card"><div class="card-body">
                    <div class="metric-value"><%=Number0(totalOrders)%></div>
                    <div class="metric-label">Orders</div>
                </div></div>
            </div>
            <div class="col-6 col-lg-4 col-xxl-2">
                <div class="card shadow-sm h-100 metric-card"><div class="card-body">
                    <div class="metric-value"><%=Number0(countryCount)%></div>
                    <div class="metric-label">Countries represented</div>
                </div></div>
            </div>
            <div class="col-6 col-lg-4 col-xxl-2">
                <div class="card shadow-sm h-100 metric-card border-primary"><div class="card-body">
                    <div class="metric-value metric-text"><%=Html(topCountryName)%></div>
                    <div class="metric-label">Largest market by <%=LCase(measureLabel)%></div>
                </div></div>
            </div>
            <div class="col-6 col-lg-4 col-xxl-2">
                <div class="card shadow-sm h-100 metric-card"><div class="card-body">
                    <div class="metric-value"><%=FormatNumber(topCountryShare, 1)%>%</div>
                    <div class="metric-label">Largest market share</div>
                </div></div>
            </div>
            <div class="col-6 col-lg-4 col-xxl-2">
                <div class="card shadow-sm h-100 metric-card"><div class="card-body">
                    <div class="metric-value"><%=Money0(averageOrderValue)%></div>
                    <div class="metric-label">Average order value</div>
                </div></div>
            </div>
        </div>

        <div class="card shadow-sm mb-4">
            <div class="card-header bg-dark text-white py-3">
                <div class="d-flex flex-column flex-xl-row justify-content-between align-items-xl-center gap-3">
                    <div class="chart-header-title">
                        <h2><%=Html(chartTitle)%></h2>
                        <div class="small opacity-75 mt-1"><%=Html(chartSubtitle)%></div>
                    </div>
                    <div class="d-flex flex-column flex-sm-row align-items-sm-center gap-2 chart-header-actions">
                        <% If chartView = "trend" Then %>
                        <form method="get" action="" class="d-flex align-items-center gap-2 trend-filters-form">
                            <input type="hidden" name="DateRange" value="<%=Html(dateRange)%>">
                            <input type="hidden" name="StartDate" value="<%=Html(startDate)%>">
                            <input type="hidden" name="EndDate" value="<%=Html(endDate)%>">
                            <input type="hidden" name="Measure" value="<%=Html(measure)%>">
                            <input type="hidden" name="UK" value="<%=Html(ukMode)%>">
                            <input type="hidden" name="TopLimit" value="<%=topLimit%>">
                            <input type="hidden" name="View" value="trend">
                            <% If trendComparisonMode Then %>
                            <label for="PreviousYears" class="small fw-semibold text-nowrap mb-0">Previous years</label>
                            <select class="form-select form-select-sm previous-years-select" id="PreviousYears" name="PreviousYears" onchange="this.form.submit()" aria-label="Number of previous years to compare">
                                <% For i = 1 To 10 %>
                                <option value="<%=i%>"<% If previousYears = i Then Response.Write " selected" %>><%=i%></option>
                                <% Next %>
                            </select>
                            <% Else %>
                            <input type="hidden" name="PreviousYears" value="<%=previousYears%>">
                            <% End If %>
                            <label for="TrendCountry" class="small fw-semibold text-nowrap mb-0">Country</label>
                            <select class="form-select form-select-sm country-select" id="TrendCountry" name="TrendCountry" onchange="this.form.submit()" aria-label="Country for the sales trend">
                                <option value="all"<% If trendCountry = "all" Then Response.Write " selected" %>>All countries</option>
                                <% For i = 0 To countryRowCount - 1
                                    countryListCode = CStr(countryRows(0, i))
                                    countryListName = CStr(countryRows(1, i))
                                %>
                                <option value="<%=Html(countryListCode)%>"<% If trendCountry = UCase(countryListCode) Then Response.Write " selected" %>><%=Html(countryListName)%></option>
                                <% Next %>
                            </select>
                        </form>
                        <% End If %>
                        <div class="btn-group btn-group-sm view-nav" role="group" aria-label="Chart view">
                            <a class="btn btn-light<% If chartView = "bar" Then Response.Write " active" %>" href="<%=Html(BuildViewUrl("bar"))%>"<% If chartView = "bar" Then Response.Write " aria-current=""page""" %>>Bar chart</a>
                            <a class="btn btn-light<% If chartView = "pie" Then Response.Write " active" %>" href="<%=Html(BuildViewUrl("pie"))%>"<% If chartView = "pie" Then Response.Write " aria-current=""page""" %>>Pie chart</a>
                            <a class="btn btn-light<% If chartView = "trend" Then Response.Write " active" %>" href="<%=Html(BuildViewUrl("trend"))%>"<% If chartView = "trend" Then Response.Write " aria-current=""page""" %>>Sales trend</a>
                            <a class="btn btn-light<% If chartView = "world" Then Response.Write " active" %>" href="<%=Html(BuildViewUrl("world"))%>"<% If chartView = "world" Then Response.Write " aria-current=""page""" %>>World map</a>
                            <a class="btn btn-light<% If chartView = "europe" Then Response.Write " active" %>" href="<%=Html(BuildViewUrl("europe"))%>"<% If chartView = "europe" Then Response.Write " aria-current=""page""" %>>Europe map</a>
                        </div>
                    </div>
                </div>
            </div>
            <div class="card-body p-2 p-md-3">
                <div class="chart-shell">
                    <div id="chart-loading" class="chart-loading">
                        <div class="text-center">
                            <div class="spinner-border text-primary mb-2" role="status"><span class="visually-hidden">Loading chart</span></div>
                            <div class="small text-muted">Loading chart&hellip;</div>
                        </div>
                    </div>
                    <div id="sales-chart" aria-label="<%=Html(chartTitle)%>"></div>
                </div>
                <% If chartView = "pie" And rowCount > pieCount Then %>
                    <div class="small-note px-2 pb-1">The pie chart shows the top <%=pieCount%> countries; the remainder are grouped as Other.</div>
                <% ElseIf chartView = "bar" And rowCount > barCount Then %>
                    <div class="small-note px-2 pb-1">The bar chart shows the top <%=barCount%> countries.</div>
                <% ElseIf chartView = "trend" Then %>
                    <% If trendComparisonMode Then %>
                    <% If previousYears = 1 Then %>
                    <div class="small-note px-2 pb-1">The trend chart compares <%=trendEarliestYear%>, <%=trendSelectedYear%> and <%=trendCurrentYear%> year to date. <%=Html(MonthName(trendCurrentMonth) & " " & trendCurrentYear)%> is a partial month.</div>
                    <% Else %>
                    <div class="small-note px-2 pb-1">The trend chart compares <%=trendEarliestYear%> to <%=trendSelectedYear%> and <%=trendCurrentYear%> year to date. <%=Html(MonthName(trendCurrentMonth) & " " & trendCurrentYear)%> is a partial month.</div>
                    <% End If %>
                    <% Else %>
                    <div class="small-note px-2 pb-1">The trend chart groups results by calendar month.</div>
                    <% End If %>
                <% End If %>
            </div>
        </div>

        <div class="card shadow-sm">
            <div class="card-header bg-primary text-white">
                <div class="d-flex justify-content-between align-items-center gap-2">
                    <h2>Country ranking</h2>
                    <span class="badge text-bg-light">Top <%=tableCount%></span>
                </div>
            </div>
            <div class="table-responsive">
                <table class="table table-striped table-hover align-middle mb-0">
                    <thead class="table-light">
                        <tr>
                            <th class="text-end">#</th>
                            <th class="country-name">Country</th>
                            <th>Code</th>
                            <th class="text-end<% If measure = "orders" Then Response.Write " table-primary" %>">Orders</th>
                            <th class="text-end<% If measure = "sales" Then Response.Write " table-primary" %>">Sales</th>
                            <th class="text-end">Share of <%=LCase(valueColumnLabel)%></th>
                        </tr>
                    </thead>
                    <tbody>
                    <% For i = 0 To tableCount - 1
                        countryCode = CStr(dataRows(0, i))
                        countryName = CStr(dataRows(1, i))
                        currentOrders = CLng(dataRows(2, i))
                        currentSales = CDbl(dataRows(3, i))
                        If measure = "orders" Then
                            currentValue = CDbl(currentOrders)
                        Else
                            currentValue = currentSales
                        End If
                        shareValue = 0
                        If totalSelected > 0 Then shareValue = (currentValue / totalSelected) * 100
                    %>
                        <tr>
                            <td class="text-end text-muted"><%=i + 1%></td>
                            <td class="fw-semibold"><%=Html(countryName)%></td>
                            <td><span class="badge text-bg-secondary"><%=Html(countryCode)%></span></td>
                            <td class="text-end"><%=Number0(currentOrders)%></td>
                            <td class="text-end"><%=Money0(currentSales)%></td>
                            <td class="text-end"><%=FormatNumber(shareValue, 1)%>%</td>
                        </tr>
                    <% Next %>
                    </tbody>
                </table>
            </div>
        </div>

    <% End If %>
</div>

<script>
(function () {
    'use strict';

    var rangeSelect = document.getElementById('DateRange');
    var startInput = document.getElementById('StartDate');
    var endInput = document.getElementById('EndDate');

    if (!rangeSelect || !startInput || !endInput) return;

    function formatDate(date) {
        var year = date.getFullYear();
        var month = String(date.getMonth() + 1).padStart(2, '0');
        var day = String(date.getDate()).padStart(2, '0');
        return year + '-' + month + '-' + day;
    }

    function addDays(date, days) {
        var result = new Date(date.getFullYear(), date.getMonth(), date.getDate());
        result.setDate(result.getDate() + days);
        return result;
    }

    function addMonths(date, months) {
        var day = date.getDate();
        var result = new Date(date.getFullYear(), date.getMonth(), 1);
        result.setMonth(result.getMonth() + months);
        var lastDay = new Date(result.getFullYear(), result.getMonth() + 1, 0).getDate();
        result.setDate(Math.min(day, lastDay));
        return result;
    }

    function setCustomMode(isCustom) {
        startInput.readOnly = !isCustom;
        endInput.readOnly = !isCustom;
        startInput.required = isCustom;
        endInput.required = isCustom;
    }

    function applySelectedRange() {
        var today = new Date();
        var value = rangeSelect.value;
        var start = null;
        var end = today;

        if (value === 'custom') {
            setCustomMode(true);
            if (!startInput.value) startInput.value = formatDate(addMonths(today, -12));
            if (!endInput.value) endInput.value = formatDate(today);
            return;
        }

        setCustomMode(false);

        switch (value) {
            case '30days': start = addDays(today, -29); break;
            case '60days': start = addDays(today, -59); break;
            case '90days': start = addDays(today, -89); break;
            case 'thisquarter':
                start = new Date(today.getFullYear(), Math.floor(today.getMonth() / 3) * 3, 1);
                break;
            case 'previousquarter':
                end = new Date(today.getFullYear(), Math.floor(today.getMonth() / 3) * 3, 0);
                start = new Date(end.getFullYear(), end.getMonth() - 2, 1);
                break;
            case '6months': start = addMonths(today, -6); break;
            case '12months': start = addMonths(today, -12); break;
            case 'yeartodate':
                start = new Date(today.getFullYear(), 0, 1);
                break;
            case 'lastyear':
                start = new Date(today.getFullYear() - 1, 0, 1);
                end = new Date(today.getFullYear() - 1, 11, 31);
                break;
            case '2years': start = addMonths(today, -24); break;
            case '3years': start = addMonths(today, -36); break;
            case '5years': start = addMonths(today, -60); break;
            case 'all':
                startInput.value = '';
                endInput.value = formatDate(today);
                return;
            default:
                start = addMonths(today, -12);
        }

        startInput.value = formatDate(start);
        endInput.value = formatDate(end);
    }

    rangeSelect.addEventListener('change', applySelectedRange);
}());
</script>

<% If validationMessage = "" And dataError = "" And rowCount > 0 Then %>
<script>
(function () {
    'use strict';

    var currentView = '<%=JsString(chartView)%>';
    var currentMeasure = '<%=JsString(measure)%>';
    var chartElement = document.getElementById('sales-chart');
    var loadingElement = document.getElementById('chart-loading');
    var resizeTimer = null;
    var chartsLoaded = false;
    var comparisonPreviousYears = <%=previousYears%>;

    function buildComparisonSeriesOptions() {
        var series = {};
        var i;

        for (i = 0; i < comparisonPreviousYears; i += 1) {
            series[i] = {lineWidth: 2, pointSize: 3};
        }

        series[comparisonPreviousYears] = {lineWidth: 4, pointSize: 5};
        series[comparisonPreviousYears + 1] = {lineWidth: 3, pointSize: 5, lineDashStyle: [8, 4]};
        return series;
    }

    function buildComparisonColors() {
        var historicalPalette = ['#8e44ad', '#16a085', '#c0392b', '#2980b9', '#f39c12', '#27ae60', '#d35400', '#e83e8c', '#6f42c1', '#17a2b8'];
        var colors = [];
        var startIndex = historicalPalette.length - comparisonPreviousYears;
        var i;

        for (i = startIndex; i < historicalPalette.length; i += 1) {
            colors.push(historicalPalette[i]);
        }

        colors.push('#0d6efd');
        colors.push('#fd7e14');
        return colors;
    }

    google.charts.load('current', {
        packages: ['geochart', 'corechart'],
        language: 'en-GB'
    });
    google.charts.setOnLoadCallback(function () {
        chartsLoaded = true;
        drawSalesChart();
    });

    function formatValueColumns(data, columnIndexes) {
        var formatter;
        var i;
        if (currentMeasure === 'orders') {
            formatter = new google.visualization.NumberFormat({fractionDigits: 0});
        } else {
            formatter = new google.visualization.NumberFormat({prefix: '£', fractionDigits: 0});
        }
        for (i = 0; i < columnIndexes.length; i += 1) {
            formatter.format(data, columnIndexes[i]);
        }
    }

    function addFormatting(data) {
        formatValueColumns(data, [1]);
    }

    function buildMapData() {
        var data = new google.visualization.DataTable();
        data.addColumn('string', 'Country');
        data.addColumn('number', '<%=JsString(valueColumnLabel)%>');
        <% For i = 0 To rowCount - 1
            countryCode = CStr(dataRows(0, i))
            countryName = CStr(dataRows(1, i))
            If measure = "orders" Then
                currentValue = CDbl(dataRows(2, i))
            Else
                currentValue = CDbl(dataRows(3, i))
            End If
        %>
        data.addRow([{v: '<%=JsString(countryCode)%>', f: '<%=JsString(countryName)%>'}, <%=JsNumber(currentValue)%>]);
        <% Next %>
        addFormatting(data);
        return data;
    }

    function buildBarData() {
        var data = new google.visualization.DataTable();
        data.addColumn('string', 'Country');
        data.addColumn('number', '<%=JsString(valueColumnLabel)%>');
        <% For i = 0 To barCount - 1
            countryName = CStr(dataRows(1, i))
            If measure = "orders" Then
                currentValue = CDbl(dataRows(2, i))
            Else
                currentValue = CDbl(dataRows(3, i))
            End If
        %>
        data.addRow(['<%=JsString(countryName)%>', <%=JsNumber(currentValue)%>]);
        <% Next %>
        addFormatting(data);
        return data;
    }

    function buildPieData() {
        var data = new google.visualization.DataTable();
        data.addColumn('string', 'Country');
        data.addColumn('number', '<%=JsString(valueColumnLabel)%>');
        <% For i = 0 To pieCount - 1
            countryName = CStr(dataRows(1, i))
            If measure = "orders" Then
                currentValue = CDbl(dataRows(2, i))
            Else
                currentValue = CDbl(dataRows(3, i))
            End If
        %>
        data.addRow(['<%=JsString(countryName)%>', <%=JsNumber(currentValue)%>]);
        <% Next %>
        <% If pieOtherValue > 0 Then %>
        data.addRow(['Other', <%=JsNumber(pieOtherValue)%>]);
        <% End If %>
        addFormatting(data);
        return data;
    }

    function buildTrendData() {
        var data = new google.visualization.DataTable();
        <% If trendComparisonMode Then %>
        data.addColumn('string', 'Month');
        <% For trendHistoricalYear = trendEarliestYear To trendSelectedYear %>
        data.addColumn('number', '<%=trendHistoricalYear%>');
        <% Next %>
        data.addColumn('number', '<%=trendCurrentYear%> year to date');
        data.addColumn({type: 'string', role: 'annotation'});
        <% For i = 1 To 12 %>
        data.addRow([
            '<%=MonthName(i, True)%>',
            <% For trendYearIndex = 0 To previousYears %>
            <%=JsNumber(trendHistoricalValues(trendYearIndex, i))%>,
            <% Next %>
            <% If i <= trendCurrentMonth Then %><%=JsNumber(trendCurrentValues(i))%><% Else %>null<% End If %>,
            <% If i = trendCurrentMonth Then %>'Partial'<% Else %>null<% End If %>
        ]);
        <% Next %>
        formatValueColumns(data, [<% For i = 1 To previousYears + 2 %><% If i > 1 Then Response.Write ", " %><%=i%><% Next %>]);
        <% Else %>
        data.addColumn('date', 'Month');
        data.addColumn('number', '<%=JsString(valueColumnLabel)%>');
        <% For i = 0 To trendRowCount - 1
            trendYear = CInt(trendRows(0, i))
            trendMonth = CInt(trendRows(1, i))
            If measure = "orders" Then
                currentValue = CDbl(trendRows(2, i))
            Else
                currentValue = CDbl(trendRows(3, i))
            End If
        %>
        data.addRow([new Date(<%=trendYear%>, <%=trendMonth - 1%>, 1), <%=JsNumber(currentValue)%>]);
        <% Next %>
        new google.visualization.DateFormat({pattern: 'MMM yyyy'}).format(data, 0);
        addFormatting(data);
        <% End If %>
        return data;
    }

    function hideLoading() {
        if (loadingElement) loadingElement.classList.add('d-none');
    }

    function drawSalesChart() {
        if (!chartsLoaded || !chartElement) return;

        var width = Math.max(chartElement.clientWidth, 320);
        var height;
        var data;
        var chart;
        var options;

        if (currentView === 'bar') {
            height = Math.max(440, <%=barCount%> * 38 + 90);
            chartElement.style.height = height + 'px';
            data = buildBarData();
            chart = new google.visualization.BarChart(chartElement);
            options = {
                width: width,
                height: height,
                backgroundColor: 'transparent',
                legend: 'none',
                chartArea: {left: 180, top: 25, width: '70%', height: '86%'},
                hAxis: {
                    minValue: 0,
                    format: currentMeasure === 'orders' ? '#,##0' : '£#,##0',
                    textStyle: {fontSize: 12}
                },
                vAxis: {textStyle: {fontSize: 13}},
                colors: ['#0d6efd']
            };
        } else if (currentView === 'pie') {
            height = Math.max(480, Math.min(620, Math.round(width * 0.56)));
            chartElement.style.height = height + 'px';
            data = buildPieData();
            chart = new google.visualization.PieChart(chartElement);
            options = {
                width: width,
                height: height,
                backgroundColor: 'transparent',
                chartArea: {left: 20, top: 25, width: '88%', height: '86%'},
                legend: {position: width < 700 ? 'bottom' : 'right', textStyle: {fontSize: 13}},
                pieSliceText: 'percentage',
                sliceVisibilityThreshold: 0,
                tooltip: {text: 'both'},
                colors: ['#0d6efd', '#dc3545', '#fd7e14', '#198754', '#6f42c1', '#0dcaf0', '#ffc107', '#6610f2', '#20c997', '#6c757d', '#adb5bd']
            };
        } else if (currentView === 'trend') {
            height = Math.max(480, Math.min(720, Math.round(width * 0.50) + <% If trendComparisonMode Then %>Math.max(0, Math.ceil((comparisonPreviousYears + 2) / 5) - 1) * 30<% Else %>0<% End If %>));
            chartElement.style.height = height + 'px';
            data = buildTrendData();
            chart = new google.visualization.LineChart(chartElement);
            options = {
                width: width,
                height: height,
                backgroundColor: 'transparent',
                legend: <% If trendComparisonMode Then %>{position: 'top', alignment: 'center', maxLines: 3, textStyle: {fontSize: 12}}<% Else %>'none'<% End If %>,
                chartArea: {left: width < 700 ? 72 : 95, top: <% If trendComparisonMode Then %>55 + Math.max(0, Math.ceil((comparisonPreviousYears + 2) / 5) - 1) * 24<% Else %>25<% End If %>, width: width < 700 ? '76%' : '84%', height: <% If trendComparisonMode Then Response.Write "64" Else Response.Write "72" %> + '%'},
                hAxis: {
                    <% If Not trendComparisonMode Then %>format: 'MMM yyyy',
                    gridlines: {count: Math.min(12, Math.max(4, <%=trendRowCount%>))},
                    minorGridlines: {count: 0},<% End If %>
                    textStyle: {fontSize: 12}
                },
                vAxis: {
                    minValue: 0,
                    format: currentMeasure === 'orders' ? '#,##0' : '£#,##0',
                    textStyle: {fontSize: 12}
                },
                <% If trendComparisonMode Then %>
                series: buildComparisonSeriesOptions(),
                annotations: {
                    style: 'point',
                    textStyle: {fontSize: 11, auraColor: 'none'}
                },
                colors: buildComparisonColors(),
                interpolateNulls: false,
                <% Else %>
                lineWidth: 3,
                pointSize: <% If trendRowCount <= 36 Then Response.Write 5 Else Response.Write 2 %>,
                colors: ['#0d6efd'],
                <% End If %>
                focusTarget: 'category'
            };
        } else {
            height = currentView === 'europe'
                ? Math.max(480, Math.min(720, Math.round(width * 0.62)))
                : Math.max(460, Math.min(680, Math.round(width * 0.52)));
            chartElement.style.height = height + 'px';
            data = buildMapData();
            chart = new google.visualization.GeoChart(chartElement);
            options = {
                width: width,
                height: height,
                region: currentView === 'europe' ? '150' : 'world',
                backgroundColor: '#eaf6fd',
                datalessRegionColor: '#f3f4f6',
                defaultColor: '#f3f4f6',
                colorAxis: {minValue: 0, colors: ['#d9f4d2', '#6aaa5d', '#1f6417']},
                legend: {textStyle: {fontSize: 12}},
                tooltip: {textStyle: {fontSize: 13}}
            };
        }

        google.visualization.events.addListener(chart, 'ready', hideLoading);
        chart.draw(data, options);
    }

    window.addEventListener('resize', function () {
        if (!chartsLoaded) return;
        window.clearTimeout(resizeTimer);
        resizeTimer = window.setTimeout(drawSalesChart, 180);
    });
}());
</script>
<% End If %>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/js/bootstrap.bundle.min.js" integrity="sha384-FKyoEForCGlyvwx9Hj09JcYn3nv7wiPVlz7YYwJrWVcXK/BmnVDxM+D2scQbITxI" crossorigin="anonymous"></script>
</body>
</html>