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-v1.asp
<%
Option Explicit
Response.Buffer = True
Response.CodePage = 65001
Response.CharSet = "utf-8"

' ======================================================================
' HyperFlight Sales Map
' 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 startDate, endDate, measure, chartView, ukMode, topLimit
Dim validationMessage, dataError, rangeLabel
Dim sql, orderBySql, countryCodeExpr, countryNameExpr
Dim dataRows, rowCount, i, tableCount, barCount, pieCount
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 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 = "?StartDate=" & Server.URLEncode(startDate) & _
                   "&EndDate=" & Server.URLEncode(endDate) & _
                   "&Measure=" & Server.URLEncode(measure) & _
                   "&UK=" & Server.URLEncode(ukMode) & _
                   "&TopLimit=" & topLimit & _
                   "&View=" & Server.URLEncode(requestedView)
End Function

' ----------------------------------------------------------------------
' Filters and defaults
' ----------------------------------------------------------------------
startDate = Trim(Request.QueryString("StartDate"))
endDate = Trim(Request.QueryString("EndDate"))
measure = LCase(Trim(Request.QueryString("Measure")))
chartView = LCase(Trim(Request.QueryString("View")))
ukMode = LCase(Trim(Request.QueryString("UK")))
topLimit = Trim(Request.QueryString("TopLimit"))

If startDate = "" Then startDate = IsoDate(DateAdd("yyyy", -1, Date()))
If endDate = "" Then endDate = IsoDate(Date())
If measure = "" Then measure = "sales"
If chartView = "" Then chartView = "world"
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

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

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

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

validationMessage = ""
If 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

rangeLabel = startDate & " to " & endDate

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 Else
        chartTitle = "World map"
End Select
chartSubtitle = measureLabel & " by country"

' ----------------------------------------------------------------------
' Query and aggregate data
' ----------------------------------------------------------------------
rowCount = 0
totalOrders = 0
totalSales = 0
totalSelected = 0
countryCount = 0
topCountryName = ""
topCountryValue = 0
topCountryShare = 0
averageOrderValue = 0
pieOtherValue = 0
dataError = ""

If validationMessage = "" Then
    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)) AS SalesTotal " & _
          "FROM orders o " & _
          "INNER JOIN countries c ON c.Country = o.Country " & _
          "WHERE o.Status <> 'CANCELLED' " & _
          "AND o.Status <> 'ORDER PLACED' " & _
          "AND o.DateTimeOrdered >= ? " & _
          "AND o.DateTimeOrdered < 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
        cmd.Parameters.Append cmd.CreateParameter("@DateFrom", adVarChar, adParamInput, 10, startDate)
        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

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);
        }
        .view-nav .btn { min-width: 110px; }
        .country-name { min-width: 190px; }
        .table th { white-space: nowrap; }
        .small-note { font-size: 0.875rem; color: #6c757d; }
        @media (max-width: 767.98px) {
            .chart-shell, #sales-chart { min-height: 420px; }
            .view-nav .btn { min-width: 0; }
        }
    </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"><%=Html(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="StartDate" class="form-label fw-semibold">Start date</label>
                    <input type="date" class="form-control" id="StartDate" name="StartDate" value="<%=Html(startDate)%>" required>
                </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)%>" required>
                </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-3">
                    <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 United Kingdom</option>
                        <option value="exclude" <% If ukMode = "exclude" Then Response.Write "selected" %>>Exclude United Kingdom</option>
                    </select>
                </div>
                <div class="col-sm-6 col-lg-2">
                    <label for="TopLimit" class="form-label fw-semibold">Top countries</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-6 col-lg-1 d-grid">
                    <input type="hidden" name="View" value="<%=Html(chartView)%>">
                    <button type="submit" class="btn btn-primary fw-semibold">Run</button>
                </div>
            </form>
        </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>
                        <h2><%=Html(chartTitle)%></h2>
                        <div class="small opacity-75 mt-1"><%=Html(chartSubtitle)%></div>
                    </div>
                    <div class="btn-group btn-group-sm view-nav flex-wrap" role="group" aria-label="Chart view">
                        <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>
                        <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>
                    </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>
                <% 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>

<% 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;

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

    function addFormatting(data) {
        var formatter;
        if (currentMeasure === 'orders') {
            formatter = new google.visualization.NumberFormat({fractionDigits: 0});
        } else {
            formatter = new google.visualization.NumberFormat({prefix: '£', fractionDigits: 0});
        }
        formatter.format(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 = barCount - 1 To 0 Step -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 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 {
            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>