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

' ======================================================================
' HyperFlight Product Analysis v3
' Product analysis page for the standalone Sales Analytics application
' Bootstrap 5 / Classic ASP / MySQL
' ======================================================================

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

Dim conn, cmd, rs
Dim countryCmd, countryRs, categoryCmd, categoryRs, summaryCmd, summaryRs
Dim subcategoryCmd, subcategoryRs, trendCmd, trendRs
Dim startDate, endDate, requestedStartDate, requestedEndDate, dateRange, dateRangeName
Dim measure, chartView, ukMode, topLimit, breakdown, countryFilter, categoryFilter
Dim trendInterval, trendCategory, trendSubcategory, yearlyTrendAvailable
Dim validationMessage, dataError, rangeLabel
Dim sql, countrySql, categorySql, subcategorySql, summarySql, trendSql, orderBySql
Dim joinsSql, whereSql, categoryExpr, subcategoryExpr, productCodeExpr, productNameExpr, productLabelExpr
Dim effectiveCountryExpr, countryCodeExpr, countryNameExpr
Dim grossBasisExpr, vatAmountExpr, discountFactorExpr, exVatFactorExpr, lineSalesExpr
Dim dataRows, rowCount, countryRows, countryRowCount, categoryRows, categoryRowCount
Dim subcategoryRows, subcategoryRowCount, trendRows, trendRowCount
Dim trendYear, trendMonth
Dim i, tableCount, barCount, pieCount
Dim currentCategory, currentSubcategory, currentGroupLabel
Dim currentOrders, currentQuantity, currentSales, currentValue, shareValue
Dim totalOrders, totalQuantity, totalSales, totalSelected, productCount
Dim topGroupName, topGroupValue, topGroupShare
Dim pieOtherValue, pieDisplayedTotal
Dim measureLabel, valueColumnLabel, groupLabel, chartTitle, chartSubtitle
Dim countryListCode, countryListName, countryFound, countryFilterName
Dim categoryListName, categoryFound, trendCategoryFound
Dim subcategoryListName, trendSubcategoryFound

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) & _
                   "&Country=" & Server.URLEncode(countryFilter) & _
                   "&Breakdown=" & Server.URLEncode(breakdown) & _
                   "&Category=" & Server.URLEncode(categoryFilter) & _
                   "&TopLimit=" & topLimit & _
                   "&TrendInterval=" & Server.URLEncode(trendInterval) & _
                   "&TrendCategory=" & Server.URLEncode(trendCategory) & _
                   "&TrendSubcategory=" & Server.URLEncode(trendSubcategory) & _
                   "&View=" & Server.URLEncode(requestedView)
End Function

Function BuildTrendIntervalUrl(ByVal requestedInterval)
    BuildTrendIntervalUrl = "?DateRange=" & Server.URLEncode(dateRange) & _
                            "&StartDate=" & Server.URLEncode(startDate) & _
                            "&EndDate=" & Server.URLEncode(endDate) & _
                            "&Measure=" & Server.URLEncode(measure) & _
                            "&UK=" & Server.URLEncode(ukMode) & _
                            "&Country=" & Server.URLEncode(countryFilter) & _
                            "&Breakdown=" & Server.URLEncode(breakdown) & _
                            "&Category=" & Server.URLEncode(categoryFilter) & _
                            "&TopLimit=" & topLimit & _
                            "&TrendInterval=" & Server.URLEncode(requestedInterval) & _
                            "&TrendCategory=" & Server.URLEncode(trendCategory) & _
                            "&TrendSubcategory=" & Server.URLEncode(trendSubcategory) & _
                            "&View=trend"
End Function

Function BuildBreakdownUrl(ByVal requestedBreakdown)
    Dim requestedCategory
    requestedCategory = categoryFilter
    If requestedBreakdown = "category" Then requestedCategory = "all"

    BuildBreakdownUrl = "?DateRange=" & Server.URLEncode(dateRange) & _
                        "&StartDate=" & Server.URLEncode(startDate) & _
                        "&EndDate=" & Server.URLEncode(endDate) & _
                        "&Measure=" & Server.URLEncode(measure) & _
                        "&UK=" & Server.URLEncode(ukMode) & _
                        "&Country=" & Server.URLEncode(countryFilter) & _
                        "&Breakdown=" & Server.URLEncode(requestedBreakdown) & _
                        "&Category=" & Server.URLEncode(requestedCategory) & _
                        "&TopLimit=" & topLimit & _
                        "&TrendInterval=" & Server.URLEncode(trendInterval) & _
                        "&TrendCategory=" & Server.URLEncode(trendCategory) & _
                        "&TrendSubcategory=" & Server.URLEncode(trendSubcategory) & _
                        "&View=" & Server.URLEncode(chartView)
End Function

Function BuildSalesMapUrl()
    BuildSalesMapUrl = "index.asp?DateRange=" & Server.URLEncode(dateRange) & _
                       "&StartDate=" & Server.URLEncode(startDate) & _
                       "&EndDate=" & Server.URLEncode(endDate) & _
                       "&Measure=sales" & _
                       "&UK=" & Server.URLEncode(ukMode) & _
                       "&TrendCountry=" & Server.URLEncode(countryFilter) & _
                       "&View=bar"
End Function

Sub AppendCommonParameters(ByRef queryCommand, ByVal includeCountry, ByVal includeCategory)
    If dateRange <> "all" Then
        queryCommand.Parameters.Append queryCommand.CreateParameter("@DateFrom", adVarChar, adParamInput, 10, startDate)
    End If
    queryCommand.Parameters.Append queryCommand.CreateParameter("@DateTo", adVarChar, adParamInput, 10, endDate)
    If includeCountry And countryFilter <> "all" Then
        queryCommand.Parameters.Append queryCommand.CreateParameter("@Country", adVarChar, adParamInput, 10, countryFilter)
    End If
    If includeCategory And categoryFilter <> "all" Then
        queryCommand.Parameters.Append queryCommand.CreateParameter("@Category", adVarChar, adParamInput, 75, categoryFilter)
    End If
End Sub

Sub AppendTrendParameters(ByRef queryCommand, ByVal includeCategory, ByVal includeSubcategory)
    If dateRange <> "all" Then
        queryCommand.Parameters.Append queryCommand.CreateParameter("@TrendDateFrom", adVarChar, adParamInput, 10, startDate)
    End If
    queryCommand.Parameters.Append queryCommand.CreateParameter("@TrendDateTo", adVarChar, adParamInput, 10, endDate)
    If countryFilter <> "all" Then
        queryCommand.Parameters.Append queryCommand.CreateParameter("@TrendCountry", adVarChar, adParamInput, 10, countryFilter)
    End If
    If includeCategory And trendCategory <> "all" Then
        queryCommand.Parameters.Append queryCommand.CreateParameter("@TrendCategory", adVarChar, adParamInput, 75, trendCategory)
    End If
    If includeSubcategory And trendSubcategory <> "all" Then
        queryCommand.Parameters.Append queryCommand.CreateParameter("@TrendSubcategory", adVarChar, adParamInput, 75, trendSubcategory)
    End If
End Sub

' ----------------------------------------------------------------------
' 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"))
breakdown = LCase(Trim(Request.QueryString("Breakdown")))
countryFilter = Trim(Request.QueryString("Country"))
categoryFilter = Trim(Request.QueryString("Category"))
trendInterval = LCase(Trim(Request.QueryString("TrendInterval")))
trendCategory = Trim(Request.QueryString("TrendCategory"))
trendSubcategory = Trim(Request.QueryString("TrendSubcategory"))

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 breakdown = "" Then breakdown = "category"
If topLimit = "" Or Not IsNumeric(topLimit) Then topLimit = 15
If countryFilter = "" Or LCase(countryFilter) = "all" Then countryFilter = "all" Else countryFilter = UCase(countryFilter)
If categoryFilter = "" Or LCase(categoryFilter) = "all" Then categoryFilter = "all"
If trendInterval = "" Then trendInterval = "monthly"
If trendCategory = "" Or LCase(trendCategory) = "all" Then trendCategory = "all"
If trendSubcategory = "" Or LCase(trendSubcategory) = "all" Then trendSubcategory = "all"
If trendCategory = "all" Then trendSubcategory = "all"

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

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

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

Select Case trendInterval
    Case "monthly", "yearly"
        ' Valid.
    Case Else
        trendInterval = "monthly"
End Select

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

Select Case breakdown
    Case "category", "subcategory", "product"
        ' Valid.
    Case Else
        breakdown = "category"
End Select

If breakdown = "category" Then categoryFilter = "all"

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

yearlyTrendAvailable = False
If validationMessage = "" Then
    If dateRange = "all" Then
        yearlyTrendAvailable = True
    ElseIf DateDiff("d", ParseIsoDate(startDate), ParseIsoDate(endDate)) >= 365 Then
        yearlyTrendAvailable = True
    End If
End If
If Not yearlyTrendAvailable And trendInterval = "yearly" Then trendInterval = "monthly"

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 = "quantity" Then
    orderBySql = "QuantityTotal"
    measureLabel = "Quantity sold"
    valueColumnLabel = "Quantity"
Else
    orderBySql = "SalesTotal"
    measureLabel = "Sales value"
    valueColumnLabel = "Sales"
End If

If breakdown = "product" Then
    groupLabel = "Product code"
    chartTitle = "Top product codes"
    If categoryFilter = "all" Then
        chartSubtitle = measureLabel & " by product code"
    Else
        chartSubtitle = measureLabel & " by product code within " & categoryFilter
    End If
ElseIf breakdown = "subcategory" Then
    groupLabel = "Subcategory"
    If categoryFilter = "all" Then
        chartTitle = "Top subcategories"
        chartSubtitle = measureLabel & " by category and subcategory"
    Else
        chartTitle = "Top subcategories"
        chartSubtitle = measureLabel & " within " & categoryFilter
    End If
Else
    groupLabel = "Category"
    chartTitle = "Top categories"
    chartSubtitle = measureLabel & " by main category"
End If

If chartView = "pie" Then
    If breakdown = "product" Then
        chartTitle = "Product code share"
    ElseIf breakdown = "subcategory" Then
        chartTitle = "Subcategory share"
    Else
        chartTitle = "Category share"
    End If
End If

' ----------------------------------------------------------------------
' SQL expressions and joins
' ----------------------------------------------------------------------
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"
productCodeExpr = "COALESCE(NULLIF(TRIM(p.ProductCode), ''), TRIM(od.ProductCode))"
productNameExpr = "COALESCE(NULLIF(TRIM(p.ProductName), ''), TRIM(od.ProductName))"
productLabelExpr = "CONCAT(" & productCodeExpr & ", CASE WHEN " & productNameExpr & " <> '' THEN CONCAT(' - ', " & productNameExpr & ") ELSE '' END)"
categoryExpr = "COALESCE(NULLIF(TRIM(pc.Category), ''), 'Uncategorised')"
subcategoryExpr = "COALESCE(NULLIF(TRIM(pc.Subcategory), ''), 'Uncategorised')"

grossBasisExpr = "(IFNULL(o.Subtotal, 0) + IFNULL(o.Discount, 0) + IFNULL(o.Delivery, 0))"
vatAmountExpr = "(CASE WHEN IFNULL(o.VATDeducted, 0) > 0 THEN IFNULL(o.VATDeducted, 0) ELSE IFNULL(o.VATIncluded, 0) END)"
discountFactorExpr = "(CASE WHEN IFNULL(o.Subtotal, 0) <> 0 THEN 1 + (IFNULL(o.Discount, 0) / o.Subtotal) ELSE 1 END)"
exVatFactorExpr = "(CASE WHEN " & grossBasisExpr & " <> 0 THEN (" & grossBasisExpr & " - " & vatAmountExpr & ") / " & grossBasisExpr & " ELSE 1 END)"
lineSalesExpr = "(IFNULL(od.Qty, 0) * IFNULL(od.PriceEach, 0) * " & discountFactorExpr & " * " & exVatFactorExpr & ")"

joinsSql = "FROM orders o " & _
           "INNER JOIN orderdetails od ON od.OrderNo = o.OrderNo " & _
           "LEFT JOIN products p ON p.ProductID = od.ProductID " & _
           "LEFT JOIN (" & _
               "SELECT pc1.ProductCode, pc1.Category, pc1.Subcategory " & _
               "FROM productcategories pc1 " & _
               "INNER JOIN (" & _
                   "SELECT ProductCode, MIN(ProductCategoryID) AS ProductCategoryID " & _
                   "FROM productcategories " & _
                   "WHERE Main = TRUE " & _
                   "GROUP BY ProductCode" & _
               ") pcm ON pcm.ProductCategoryID = pc1.ProductCategoryID" & _
           ") pc ON pc.ProductCode = " & productCodeExpr & " " & _
           "INNER JOIN countries c ON c.Country = " & effectiveCountryExpr & " "

whereSql = "WHERE o.IsInvoice = TRUE " & _
           "AND o.PaymentReceived = TRUE " & _
           "AND o.Status <> 'CANCELLED' " & _
           "AND o.Status <> 'ORDER PLACED' " & _
           "AND NOT (IFNULL(od.PriceEach, 0) = 0 AND IFNULL(od.BundlePriceEach, 0) > 0) "

If dateRange <> "all" Then whereSql = whereSql & "AND o.DateTimePaid >= ? "
whereSql = whereSql & "AND o.DateTimePaid < DATE_ADD(?, INTERVAL 1 DAY) " & _
                      "AND TRIM(IFNULL(c.CodeA2, '')) <> '' "
If ukMode = "exclude" Then whereSql = whereSql & "AND LEFT(c.Country, 14) <> 'United Kingdom' "

' ----------------------------------------------------------------------
' Query data
' ----------------------------------------------------------------------
rowCount = 0
countryRowCount = 0
categoryRowCount = 0
subcategoryRowCount = 0
trendRowCount = 0
totalOrders = 0
totalQuantity = 0
totalSales = 0
totalSelected = 0
productCount = 0
topGroupName = ""
topGroupValue = 0
topGroupShare = 0
pieOtherValue = 0
countryFound = False
countryFilterName = "All countries"
categoryFound = False
trendCategoryFound = False
trendSubcategoryFound = False
dataError = ""

If validationMessage = "" Then
    On Error Resume Next

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

    If Err.Number <> 0 Then
        dataError = Err.Description
        Err.Clear
    End If

    ' Countries containing qualifying product lines for the selected period.
    If dataError = "" Then
        countrySql = "SELECT " & countryCodeExpr & " AS CountryCode, " & _
                     countryNameExpr & " AS CountryName " & _
                     joinsSql & whereSql & _
                     "GROUP BY " & countryCodeExpr & ", " & countryNameExpr & " " & _
                     "ORDER BY CountryName ASC"

        Set countryCmd = Server.CreateObject("ADODB.Command")
        Set countryCmd.ActiveConnection = conn
        countryCmd.CommandType = adCmdText
        countryCmd.CommandText = countrySql
        Call AppendCommonParameters(countryCmd, False, False)
        Set countryRs = countryCmd.Execute()

        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
    End If

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

        If Not countryFound Then
            countryFilter = "all"
            countryFilterName = "All countries"
        End If
    End If

    ' Main categories containing qualifying product lines after the country filter.
    If dataError = "" Then
        categorySql = "SELECT " & categoryExpr & " AS CategoryName " & _
                      joinsSql & whereSql
        If countryFilter <> "all" Then categorySql = categorySql & "AND " & countryCodeExpr & " = ? "
        categorySql = categorySql & "GROUP BY " & categoryExpr & " ORDER BY CategoryName ASC"

        Set categoryCmd = Server.CreateObject("ADODB.Command")
        Set categoryCmd.ActiveConnection = conn
        categoryCmd.CommandType = adCmdText
        categoryCmd.CommandText = categorySql
        Call AppendCommonParameters(categoryCmd, True, False)
        Set categoryRs = categoryCmd.Execute()

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

        If IsObject(categoryRs) Then
            If categoryRs.State <> 0 Then categoryRs.Close
            Set categoryRs = Nothing
        End If
        Set categoryCmd = Nothing
    End If

    If dataError = "" Then
        If categoryFilter = "all" Then
            categoryFound = True
        Else
            For i = 0 To categoryRowCount - 1
                categoryListName = CStr(categoryRows(0, i))
                If StrComp(categoryListName, categoryFilter, vbTextCompare) = 0 Then
                    categoryFound = True
                    categoryFilter = categoryListName
                    Exit For
                End If
            Next
        End If

        If Not categoryFound Then categoryFilter = "all"
        If breakdown = "category" Then categoryFilter = "all"

        If trendCategory = "all" Then
            trendCategoryFound = True
        Else
            For i = 0 To categoryRowCount - 1
                categoryListName = CStr(categoryRows(0, i))
                If StrComp(categoryListName, trendCategory, vbTextCompare) = 0 Then
                    trendCategoryFound = True
                    trendCategory = categoryListName
                    Exit For
                End If
            Next
        End If

        If Not trendCategoryFound Then trendCategory = "all"
        If trendCategory = "all" Then trendSubcategory = "all"
    End If

    ' Subcategories available to the trend filter for the selected category.
    If dataError = "" And chartView = "trend" And trendCategory <> "all" Then
        subcategorySql = "SELECT " & subcategoryExpr & " AS SubcategoryName " & _
                         joinsSql & whereSql
        If countryFilter <> "all" Then subcategorySql = subcategorySql & "AND " & countryCodeExpr & " = ? "
        subcategorySql = subcategorySql & "AND " & categoryExpr & " = ? " & _
                                           "GROUP BY " & subcategoryExpr & " ORDER BY SubcategoryName ASC"

        Set subcategoryCmd = Server.CreateObject("ADODB.Command")
        Set subcategoryCmd.ActiveConnection = conn
        subcategoryCmd.CommandType = adCmdText
        subcategoryCmd.CommandText = subcategorySql
        Call AppendTrendParameters(subcategoryCmd, True, False)
        Set subcategoryRs = subcategoryCmd.Execute()

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

        If IsObject(subcategoryRs) Then
            If subcategoryRs.State <> 0 Then subcategoryRs.Close
            Set subcategoryRs = Nothing
        End If
        Set subcategoryCmd = Nothing
    End If

    If dataError = "" Then
        If trendSubcategory = "all" Then
            trendSubcategoryFound = True
        ElseIf trendCategory <> "all" Then
            For i = 0 To subcategoryRowCount - 1
                subcategoryListName = CStr(subcategoryRows(0, i))
                If StrComp(subcategoryListName, trendSubcategory, vbTextCompare) = 0 Then
                    trendSubcategoryFound = True
                    trendSubcategory = subcategoryListName
                    Exit For
                End If
            Next
        End If

        If Not trendSubcategoryFound Then trendSubcategory = "all"
    End If

    ' Category, subcategory or product-code totals.
    If dataError = "" Then
        If breakdown = "product" Then
            sql = "SELECT " & categoryExpr & " AS CategoryName, " & _
                  productLabelExpr & " AS SubcategoryName, " & _
                  "COUNT(DISTINCT o.OrderNo) AS OrderCount, " & _
                  "SUM(IFNULL(od.Qty, 0)) AS QuantityTotal, " & _
                  "SUM(" & lineSalesExpr & ") AS SalesTotal " & _
                  joinsSql & whereSql
            If countryFilter <> "all" Then sql = sql & "AND " & countryCodeExpr & " = ? "
            If categoryFilter <> "all" Then sql = sql & "AND " & categoryExpr & " = ? "
            sql = sql & "GROUP BY " & categoryExpr & ", " & productCodeExpr & ", " & productNameExpr & " " & _
                        "ORDER BY " & orderBySql & " DESC, CategoryName ASC, SubcategoryName ASC"
        ElseIf breakdown = "subcategory" Then
            sql = "SELECT " & categoryExpr & " AS CategoryName, " & _
                  subcategoryExpr & " AS SubcategoryName, " & _
                  "COUNT(DISTINCT o.OrderNo) AS OrderCount, " & _
                  "SUM(IFNULL(od.Qty, 0)) AS QuantityTotal, " & _
                  "SUM(" & lineSalesExpr & ") AS SalesTotal " & _
                  joinsSql & whereSql
            If countryFilter <> "all" Then sql = sql & "AND " & countryCodeExpr & " = ? "
            If categoryFilter <> "all" Then sql = sql & "AND " & categoryExpr & " = ? "
            sql = sql & "GROUP BY " & categoryExpr & ", " & subcategoryExpr & " " & _
                        "ORDER BY " & orderBySql & " DESC, CategoryName ASC, SubcategoryName ASC"
        Else
            sql = "SELECT " & categoryExpr & " AS CategoryName, " & _
                  "'' AS SubcategoryName, " & _
                  "COUNT(DISTINCT o.OrderNo) AS OrderCount, " & _
                  "SUM(IFNULL(od.Qty, 0)) AS QuantityTotal, " & _
                  "SUM(" & lineSalesExpr & ") AS SalesTotal " & _
                  joinsSql & whereSql
            If countryFilter <> "all" Then sql = sql & "AND " & countryCodeExpr & " = ? "
            sql = sql & "GROUP BY " & categoryExpr & " " & _
                        "ORDER BY " & orderBySql & " DESC, CategoryName ASC"
        End If

        Set cmd = Server.CreateObject("ADODB.Command")
        Set cmd.ActiveConnection = conn
        cmd.CommandType = adCmdText
        cmd.CommandText = sql
        Call AppendCommonParameters(cmd, True, (breakdown <> "category"))
        Set rs = cmd.Execute()

        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
    End If

    ' Unique orders and products for the summary cards.
    If dataError = "" And rowCount > 0 Then
        summarySql = "SELECT COUNT(DISTINCT o.OrderNo) AS OrderCount, " & _
                     "COUNT(DISTINCT NULLIF(" & productCodeExpr & ", '')) AS ProductCount " & _
                     joinsSql & whereSql
        If countryFilter <> "all" Then summarySql = summarySql & "AND " & countryCodeExpr & " = ? "
        If breakdown <> "category" And categoryFilter <> "all" Then summarySql = summarySql & "AND " & categoryExpr & " = ? "

        Set summaryCmd = Server.CreateObject("ADODB.Command")
        Set summaryCmd.ActiveConnection = conn
        summaryCmd.CommandType = adCmdText
        summaryCmd.CommandText = summarySql
        Call AppendCommonParameters(summaryCmd, True, (breakdown <> "category"))
        Set summaryRs = summaryCmd.Execute()

        If Err.Number <> 0 Then
            dataError = Err.Description
            Err.Clear
        ElseIf Not summaryRs.EOF Then
            If Not IsNull(summaryRs("OrderCount")) Then totalOrders = CLng(summaryRs("OrderCount"))
            If Not IsNull(summaryRs("ProductCount")) Then productCount = CLng(summaryRs("ProductCount"))
        End If

        If IsObject(summaryRs) Then
            If summaryRs.State <> 0 Then summaryRs.Close
            Set summaryRs = Nothing
        End If
        Set summaryCmd = Nothing
    End If

    ' Product trend totals for the selected category or subcategory.
    If dataError = "" And rowCount > 0 And chartView = "trend" Then
        trendSql = "SELECT YEAR(o.DateTimePaid) AS SalesYear, " & _
                   "MONTH(o.DateTimePaid) AS SalesMonth, " & _
                   "SUM(IFNULL(od.Qty, 0)) AS QuantityTotal, " & _
                   "SUM(" & lineSalesExpr & ") AS SalesTotal " & _
                   joinsSql & whereSql
        If countryFilter <> "all" Then trendSql = trendSql & "AND " & countryCodeExpr & " = ? "
        If trendCategory <> "all" Then trendSql = trendSql & "AND " & categoryExpr & " = ? "
        If trendSubcategory <> "all" Then trendSql = trendSql & "AND " & subcategoryExpr & " = ? "
        trendSql = trendSql & "GROUP BY YEAR(o.DateTimePaid), MONTH(o.DateTimePaid) " & _
                              "ORDER BY SalesYear ASC, SalesMonth ASC"

        Set trendCmd = Server.CreateObject("ADODB.Command")
        Set trendCmd.ActiveConnection = conn
        trendCmd.CommandType = adCmdText
        trendCmd.CommandText = trendSql
        Call AppendTrendParameters(trendCmd, True, True)
        Set trendRs = trendCmd.Execute()

        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
    End If

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

    On Error GoTo 0
End If

If dataError = "" And rowCount > 0 Then
    For i = 0 To rowCount - 1
        currentQuantity = 0
        currentSales = 0
        If Not IsNull(dataRows(3, i)) Then currentQuantity = CDbl(dataRows(3, i))
        If Not IsNull(dataRows(4, i)) Then currentSales = CDbl(dataRows(4, i))
        totalQuantity = totalQuantity + currentQuantity
        totalSales = totalSales + currentSales
    Next

    If measure = "quantity" Then
        totalSelected = totalQuantity
    Else
        totalSelected = totalSales
    End If

    currentCategory = CStr(dataRows(0, 0))
    currentSubcategory = CStr(dataRows(1, 0))
    If breakdown = "product" Then
        topGroupName = currentSubcategory
    ElseIf breakdown = "subcategory" Then
        If categoryFilter = "all" Then
            topGroupName = currentCategory & " - " & currentSubcategory
        Else
            topGroupName = currentSubcategory
        End If
    Else
        topGroupName = currentCategory
    End If

    If measure = "quantity" Then
        topGroupValue = CDbl(dataRows(3, 0))
    Else
        topGroupValue = CDbl(dataRows(4, 0))
    End If
    If totalSelected > 0 Then topGroupShare = (topGroupValue / totalSelected) * 100
End If

If chartView = "trend" Then
    If trendInterval = "yearly" Then
        If measure = "quantity" Then
            chartTitle = "Yearly quantity trend"
        Else
            chartTitle = "Yearly product sales trend"
        End If
    Else
        If measure = "quantity" Then
            chartTitle = "Monthly quantity trend"
        Else
            chartTitle = "Monthly product sales trend"
        End If
    End If

    If trendSubcategory <> "all" Then
        chartSubtitle = measureLabel & " for " & trendSubcategory & " within " & trendCategory
    ElseIf trendCategory <> "all" Then
        chartSubtitle = measureLabel & " for " & trendCategory
    Else
        chartSubtitle = measureLabel & " across all categories"
    End If

    If countryFilter <> "all" Then chartSubtitle = chartSubtitle & " (" & countryFilterName & ")"
ElseIf countryFilter <> "all" Then
    chartSubtitle = chartSubtitle & " (" & countryFilterName & ")"
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 = "quantity" Then
            pieDisplayedTotal = pieDisplayedTotal + CDbl(dataRows(3, i))
        Else
            pieDisplayedTotal = pieDisplayedTotal + CDbl(dataRows(4, 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 Analytics - Product Analysis</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 And (chartView <> "trend" Or trendRowCount > 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.15rem; }
        .metric-label { color: #6c757d; font-size: 0.9rem; }
        .card-header h2 { font-size: 1.05rem; margin: 0; }
        .chart-shell { position: relative; min-height: 500px; }
        #product-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, .breakdown-nav { flex-wrap: nowrap !important; }
        .view-nav .btn { min-width: 92px; white-space: nowrap; }
        .breakdown-nav .btn { min-width: 96px; white-space: nowrap; }
        .category-select { width: 210px; min-width: 210px; max-width: 210px; }
        .trend-category-select { width: 190px; min-width: 190px; max-width: 190px; }
        .trend-subcategory-select { width: 210px; min-width: 210px; max-width: 210px; }
        .trend-filters-form { flex-wrap: nowrap; }
        .group-name { min-width: 190px; }
        .table th { white-space: nowrap; }
        .small-note { font-size: 0.875rem; color: #6c757d; }
        .analysis-nav .btn { min-width: 145px; }
        @media (max-width: 1199.98px) {
            .chart-header-actions { flex-wrap: wrap; }
        }
        @media (max-width: 767.98px) {
            .chart-shell, #product-chart { min-height: 420px; }
            .view-nav, .breakdown-nav { flex-wrap: wrap !important; }
            .view-nav .btn, .breakdown-nav .btn { min-width: 0; }
            .category-select, .trend-category-select, .trend-subcategory-select { width: 100%; min-width: 180px; max-width: none; }
            .trend-filters-form { flex-wrap: wrap; width: 100%; }
        }

        .busy-overlay {
            position: fixed;
            inset: 0;
            z-index: 1080;
            display: none;
            align-items: center;
            justify-content: center;
            background: rgba(255, 255, 255, 0.72);
            backdrop-filter: blur(1px);
        }
        .busy-card {
            min-width: 220px;
            border-radius: 0.75rem;
            box-shadow: 0 0.75rem 2rem rgba(0, 0, 0, 0.18);
        }
        .busy-hourglass {
            font-size: 2rem;
            line-height: 1;
        }
        body.is-busy {
            cursor: wait;
        }
        body.is-busy .busy-overlay {
            display: flex;
        }
    </style>
</head>
<body class="pb-5">
<div id="busy-overlay" class="busy-overlay" role="status" aria-live="polite" aria-hidden="true">
    <div class="busy-card bg-white border text-center p-4">
        <div class="busy-hourglass mb-2" aria-hidden="true">&#x231B;</div>
        <div class="spinner-border text-primary mb-3" role="status"><span class="visually-hidden">Processing</span></div>
        <div class="fw-semibold">Processing report&hellip;</div>
        <div class="small text-muted mt-1">Please wait</div>
    </div>
</div>
<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-3 align-items-lg-center">
                <div>
                    <h1 class="h4 mb-1">HyperFlight Sales Analytics</h1>
                    <div class="small opacity-75">Product sales by current main category, subcategory and product</div>
                </div>
                <div class="d-flex flex-column flex-sm-row align-items-sm-center gap-2">
                    <div class="btn-group btn-group-sm analysis-nav" role="group" aria-label="Analysis section">
                        <a class="btn btn-light" href="<%=Html(BuildSalesMapUrl())%>">Geographical analysis</a>
                        <span class="btn btn-light active" aria-current="page">Product analysis</span>
                    </div>
                    <span class="badge text-bg-light"><%=rangeLabel%></span>
                </div>
            </div>
        </div>
        <div class="card-body">
            <form method="get" action="" class="row g-3 align-items-end">
                <div class="col-sm-6 col-xl-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-xl-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-xl-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-xl-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="quantity" <% If measure = "quantity" Then Response.Write "selected" %>>Quantity sold</option>
                    </select>
                </div>
                <div class="col-sm-6 col-xl-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-6 col-xl-2">
                    <label for="Country" class="form-label fw-semibold">Country</label>
                    <select class="form-select" id="Country" name="Country">
                        <option value="all"<% If countryFilter = "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 countryFilter = UCase(countryListCode) Then Response.Write " selected" %>><%=Html(countryListName)%></option>
                        <% Next %>
                    </select>
                </div>
                <div class="col-sm-6 col-lg-3 col-xl-2">
                    <label for="Breakdown" class="form-label fw-semibold">Breakdown</label>
                    <select class="form-select" id="Breakdown" name="Breakdown">
                        <option value="category" <% If breakdown = "category" Then Response.Write "selected" %>>Category</option>
                        <option value="subcategory" <% If breakdown = "subcategory" Then Response.Write "selected" %>>Subcategory</option>
                        <option value="product" <% If breakdown = "product" Then Response.Write "selected" %>>Product code</option>
                    </select>
                </div>
                <div class="col-sm-6 col-lg-5 col-xl-4">
                    <label for="Category" class="form-label fw-semibold">Category filter</label>
                    <select class="form-select" id="Category" name="Category"<% If breakdown = "category" Then Response.Write " disabled" %>>
                        <option value="all"<% If categoryFilter = "all" Then Response.Write " selected" %>>All categories</option>
                        <% For i = 0 To categoryRowCount - 1
                            categoryListName = CStr(categoryRows(0, i))
                        %>
                        <option value="<%=Html(categoryListName)%>"<% If categoryFilter = categoryListName Then Response.Write " selected" %>><%=Html(categoryListName)%></option>
                        <% Next %>
                    </select>
                </div>
                <div class="col-6 col-lg-2 col-xl-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-6 col-lg-2 col-xl-1 d-grid">
                    <input type="hidden" name="View" value="<%=Html(chartView)%>">
                    <input type="hidden" name="TrendInterval" value="<%=Html(trendInterval)%>">
                    <input type="hidden" name="TrendCategory" value="<%=Html(trendCategory)%>">
                    <input type="hidden" name="TrendSubcategory" value="<%=Html(trendSubcategory)%>">
                    <button type="submit" class="btn btn-primary fw-semibold">Run</button>
                </div>
            </form>
            <div class="small-note mt-3">Sales values exclude VAT and delivery. Order discounts are allocated proportionally across product lines. Countries use the delivery address, falling back to the billing address. Partial refunds and restocked quantities are not taken into account.</div>
            <div class="small-note mt-1">Historical sales use each product's current main category. Bundle container records are excluded.</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 product 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">Product sales</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(totalQuantity)%></div>
                    <div class="metric-label">Quantity sold</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 represented</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(productCount)%></div>
                    <div class="metric-label">Products 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(topGroupName)%></div>
                    <div class="metric-label">Largest <%=LCase(groupLabel)%> 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(topGroupShare, 1)%>%</div>
                    <div class="metric-label">Largest <%=LCase(groupLabel)%> share</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="Country" value="<%=Html(countryFilter)%>">
                            <input type="hidden" name="Breakdown" value="<%=Html(breakdown)%>">
                            <input type="hidden" name="Category" value="<%=Html(categoryFilter)%>">
                            <input type="hidden" name="TopLimit" value="<%=topLimit%>">
                            <input type="hidden" name="TrendInterval" value="<%=Html(trendInterval)%>">
                            <input type="hidden" name="View" value="trend">
                            <label for="TrendCategory" class="small fw-semibold text-nowrap mb-0">Category</label>
                            <select class="form-select form-select-sm trend-category-select" id="TrendCategory" name="TrendCategory" onchange="this.form.submit()">
                                <option value="all"<% If trendCategory = "all" Then Response.Write " selected" %>>All categories</option>
                                <% For i = 0 To categoryRowCount - 1
                                    categoryListName = CStr(categoryRows(0, i))
                                %>
                                <option value="<%=Html(categoryListName)%>"<% If trendCategory = categoryListName Then Response.Write " selected" %>><%=Html(categoryListName)%></option>
                                <% Next %>
                            </select>
                            <label for="TrendSubcategory" class="small fw-semibold text-nowrap mb-0">Subcategory</label>
                            <select class="form-select form-select-sm trend-subcategory-select" id="TrendSubcategory" name="TrendSubcategory" onchange="this.form.submit()"<% If trendCategory = "all" Then Response.Write " disabled" %>>
                                <option value="all"<% If trendSubcategory = "all" Then Response.Write " selected" %>>All subcategories</option>
                                <% For i = 0 To subcategoryRowCount - 1
                                    subcategoryListName = CStr(subcategoryRows(0, i))
                                %>
                                <option value="<%=Html(subcategoryListName)%>"<% If trendSubcategory = subcategoryListName Then Response.Write " selected" %>><%=Html(subcategoryListName)%></option>
                                <% Next %>
                            </select>
                        </form>
                        <% Else %>
                        <div class="btn-group btn-group-sm breakdown-nav" role="group" aria-label="Product breakdown">
                            <a class="btn btn-light<% If breakdown = "category" Then Response.Write " active" %>" href="<%=Html(BuildBreakdownUrl("category"))%>"<% If breakdown = "category" Then Response.Write " aria-current=""page""" %>>Categories</a>
                            <a class="btn btn-light<% If breakdown = "subcategory" Then Response.Write " active" %>" href="<%=Html(BuildBreakdownUrl("subcategory"))%>"<% If breakdown = "subcategory" Then Response.Write " aria-current=""page""" %>>Subcategories</a>
                            <a class="btn btn-light<% If breakdown = "product" Then Response.Write " active" %>" href="<%=Html(BuildBreakdownUrl("product"))%>"<% If breakdown = "product" Then Response.Write " aria-current=""page""" %>>Product codes</a>
                        </div>
                        <% If breakdown = "subcategory" Or breakdown = "product" Then %>
                        <form method="get" action="" class="d-flex align-items-center gap-2">
                            <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="Country" value="<%=Html(countryFilter)%>">
                            <input type="hidden" name="Breakdown" value="<%=Html(breakdown)%>">
                            <input type="hidden" name="TopLimit" value="<%=topLimit%>">
                            <input type="hidden" name="TrendInterval" value="<%=Html(trendInterval)%>">
                            <input type="hidden" name="TrendCategory" value="<%=Html(trendCategory)%>">
                            <input type="hidden" name="TrendSubcategory" value="<%=Html(trendSubcategory)%>">
                            <input type="hidden" name="View" value="<%=Html(chartView)%>">
                            <label for="HeaderCategory" class="small fw-semibold text-nowrap mb-0">Category</label>
                            <select class="form-select form-select-sm category-select" id="HeaderCategory" name="Category" onchange="this.form.submit()">
                                <option value="all"<% If categoryFilter = "all" Then Response.Write " selected" %>>All categories</option>
                                <% For i = 0 To categoryRowCount - 1
                                    categoryListName = CStr(categoryRows(0, i))
                                %>
                                <option value="<%=Html(categoryListName)%>"<% If categoryFilter = categoryListName Then Response.Write " selected" %>><%=Html(categoryListName)%></option>
                                <% Next %>
                            </select>
                        </form>
                        <% End If %>
                        <% 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>
                        </div>
                    </div>
                </div>
            </div>
            <div class="card-body p-2 p-md-3">
                <% If chartView = "trend" Then %>
                <div class="d-flex justify-content-end align-items-center gap-2 px-2 pb-2">
                    <span class="small fw-semibold text-muted">Trend interval</span>
                    <div class="btn-group btn-group-sm" role="group" aria-label="Trend interval">
                        <a class="btn btn-outline-secondary<% If trendInterval = "monthly" Then Response.Write " active" %>" href="<%=Html(BuildTrendIntervalUrl("monthly"))%>"<% If trendInterval = "monthly" Then Response.Write " aria-current=""page""" %>>Monthly</a>
                        <% If yearlyTrendAvailable Then %>
                        <a class="btn btn-outline-secondary<% If trendInterval = "yearly" Then Response.Write " active" %>" href="<%=Html(BuildTrendIntervalUrl("yearly"))%>"<% If trendInterval = "yearly" Then Response.Write " aria-current=""page""" %>>Yearly</a>
                        <% Else %>
                        <span class="btn btn-outline-secondary disabled" aria-disabled="true" title="Yearly trend is available for date ranges of at least 12 months">Yearly</span>
                        <% End If %>
                    </div>
                </div>
                <% End If %>
                <% If chartView = "trend" And trendRowCount = 0 Then %>
                    <div class="alert alert-info mb-0">No trend data were found for the selected category and subcategory.</div>
                <% Else %>
                <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="product-chart" aria-label="<%=Html(chartTitle)%>"></div>
                </div>
                <% End If %>
                <% If chartView = "pie" And rowCount > pieCount Then %>
                    <div class="small-note px-2 pb-1">The pie chart shows the top <%=pieCount%> <%=LCase(groupLabel)%>s; 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%> <%=LCase(groupLabel)%>s.</div>
                <% ElseIf chartView = "trend" And trendRowCount > 0 Then %>
                    <% If trendInterval = "yearly" Then %>
                    <div class="small-note px-2 pb-1">The yearly trend groups results by calendar year. The first and last years may be partial where the selected dates do not cover complete calendar years.</div>
                    <% 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><%=Html(groupLabel)%> ranking</h2>
                    <span class="badge text-bg-light">Top <%=tableCount%> of <%=rowCount%></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="group-name">Category</th>
                            <% If breakdown = "subcategory" Then %><th class="group-name">Subcategory</th><% ElseIf breakdown = "product" Then %><th class="group-name">Product code</th><% End If %>
                            <th class="text-end">Orders</th>
                            <th class="text-end<% If measure = "quantity" Then Response.Write " table-primary" %>">Quantity</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
                        currentCategory = CStr(dataRows(0, i))
                        currentSubcategory = CStr(dataRows(1, i))
                        currentOrders = CLng(dataRows(2, i))
                        currentQuantity = CDbl(dataRows(3, i))
                        currentSales = CDbl(dataRows(4, i))
                        If measure = "quantity" Then currentValue = currentQuantity Else currentValue = currentSales
                        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(currentCategory)%></td>
                            <% If breakdown = "subcategory" Or breakdown = "product" Then %><td><%=Html(currentSubcategory)%></td><% End If %>
                            <td class="text-end"><%=Number0(currentOrders)%></td>
                            <td class="text-end"><%=Number0(currentQuantity)%></td>
                            <td class="text-end"><%=Money0(currentSales)%></td>
                            <td class="text-end"><%=FormatNumber(shareValue, 1)%>%</td>
                        </tr>
                    <% Next %>
                    </tbody>
                </table>
            </div>
            <div class="card-footer small-note">Order counts can overlap because one order may contain products from more than one <%=LCase(groupLabel)%>.</div>
        </div>

    <% End If %>
</div>

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

    var rangeSelect = document.getElementById('DateRange');
    var startInput = document.getElementById('StartDate');
    var endInput = document.getElementById('EndDate');
    var breakdownSelect = document.getElementById('Breakdown');
    var categorySelect = document.getElementById('Category');

    if (breakdownSelect && categorySelect) {
        breakdownSelect.addEventListener('change', function () {
            categorySelect.disabled = breakdownSelect.value === 'category';
            if (categorySelect.disabled) categorySelect.value = 'all';
        });
    }

    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 And (chartView <> "trend" Or trendRowCount > 0) Then %>
<script>
(function () {
    'use strict';

    var currentView = '<%=JsString(chartView)%>';
    var currentMeasure = '<%=JsString(measure)%>';
    var currentTrendInterval = '<%=JsString(trendInterval)%>';
    var currentCalendarYear = <%=Year(Date())%>;
    var trendEndsToday = <%=LCase(CStr(endDate = IsoDate(Date())))%>;
    var chartElement = document.getElementById('product-chart');
    var loadingElement = document.getElementById('chart-loading');
    var resizeTimer = null;
    var chartsLoaded = false;

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

    function addFormatting(data) {
        var formatter;
        if (currentMeasure === 'quantity') {
            formatter = new google.visualization.NumberFormat({fractionDigits: 0});
        } else {
            formatter = new google.visualization.NumberFormat({prefix: '£', fractionDigits: 0});
        }
        formatter.format(data, 1);
    }

    function buildBarData() {
        var data = new google.visualization.DataTable();
        data.addColumn('string', '<%=JsString(groupLabel)%>');
        data.addColumn('number', '<%=JsString(valueColumnLabel)%>');
        <% For i = 0 To barCount - 1
            currentCategory = CStr(dataRows(0, i))
            currentSubcategory = CStr(dataRows(1, i))
            If breakdown = "product" Then
                If categoryFilter = "all" Then
                    currentGroupLabel = currentCategory & " - " & currentSubcategory
                Else
                    currentGroupLabel = currentSubcategory
                End If
            ElseIf breakdown = "subcategory" Then
                If categoryFilter = "all" Then
                    currentGroupLabel = currentCategory & " - " & currentSubcategory
                Else
                    currentGroupLabel = currentSubcategory
                End If
            Else
                currentGroupLabel = currentCategory
            End If
            If measure = "quantity" Then currentValue = CDbl(dataRows(3, i)) Else currentValue = CDbl(dataRows(4, i))
        %>
        data.addRow(['<%=JsString(currentGroupLabel)%>', <%=JsNumber(currentValue)%>]);
        <% Next %>
        addFormatting(data);
        return data;
    }

    function buildPieData() {
        var data = new google.visualization.DataTable();
        data.addColumn('string', '<%=JsString(groupLabel)%>');
        data.addColumn('number', '<%=JsString(valueColumnLabel)%>');
        <% For i = 0 To pieCount - 1
            currentCategory = CStr(dataRows(0, i))
            currentSubcategory = CStr(dataRows(1, i))
            If breakdown = "product" Then
                If categoryFilter = "all" Then
                    currentGroupLabel = currentCategory & " - " & currentSubcategory
                Else
                    currentGroupLabel = currentSubcategory
                End If
            ElseIf breakdown = "subcategory" Then
                If categoryFilter = "all" Then
                    currentGroupLabel = currentCategory & " - " & currentSubcategory
                Else
                    currentGroupLabel = currentSubcategory
                End If
            Else
                currentGroupLabel = currentCategory
            End If
            If measure = "quantity" Then currentValue = CDbl(dataRows(3, i)) Else currentValue = CDbl(dataRows(4, i))
        %>
        data.addRow(['<%=JsString(currentGroupLabel)%>', <%=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 trendInterval = "yearly" Then %>
        var yearlyValues = {};
        var yearOrder = [];
        var yearKey;
        var i;

        data.addColumn('string', 'Year');
        data.addColumn('number', '<%=JsString(valueColumnLabel)%>');
        data.addColumn({type: 'string', role: 'style'});
        <% For i = 0 To trendRowCount - 1
            trendYear = CInt(trendRows(0, i))
            If measure = "quantity" Then currentValue = CDbl(trendRows(2, i)) Else currentValue = CDbl(trendRows(3, i))
        %>
        yearKey = '<%=trendYear%>';
        if (!Object.prototype.hasOwnProperty.call(yearlyValues, yearKey)) {
            yearlyValues[yearKey] = 0;
            yearOrder.push(yearKey);
        }
        yearlyValues[yearKey] += <%=JsNumber(currentValue)%>;
        <% Next %>

        for (i = 0; i < yearOrder.length; i += 1) {
            yearKey = yearOrder[i];
            data.addRow([
                trendEndsToday && yearKey === String(currentCalendarYear) ? yearKey + ' YTD' : yearKey,
                yearlyValues[yearKey],
                trendEndsToday && yearKey === String(currentCalendarYear) ? 'color: #fd7e14' : 'color: #0d6efd'
            ]);
        }
        addFormatting(data);
        <% 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 = "quantity" Then currentValue = CDbl(trendRows(2, i)) Else currentValue = CDbl(trendRows(3, i))
        %>
        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 drawProductChart() {
        if (!chartsLoaded || !chartElement) return;

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

        if (currentView === 'pie') {
            height = Math.max(480, Math.min(650, 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)));
            chartElement.style.height = height + 'px';
            data = buildTrendData();
            chart = currentTrendInterval === 'yearly'
                ? new google.visualization.ColumnChart(chartElement)
                : new google.visualization.LineChart(chartElement);
            options = {
                width: width,
                height: height,
                backgroundColor: 'transparent',
                legend: 'none',
                chartArea: {left: width < 700 ? 72 : 95, top: 25, width: width < 700 ? '76%' : '84%', height: '72%'},
                hAxis: {
                    <% If trendInterval = "monthly" 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 === 'quantity' ? '#,##0' : '£#,##0',
                    textStyle: {fontSize: 12}
                },
                <% If trendInterval = "yearly" Then %>
                bar: {groupWidth: '62%'},
                colors: ['#0d6efd'],
                <% Else %>
                lineWidth: 3,
                pointSize: <% If trendRowCount <= 36 Then Response.Write 5 Else Response.Write 2 %>,
                colors: ['#0d6efd'],
                <% End If %>
                focusTarget: 'category'
            };
        } else {
            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: width < 700 ? 150 : 255, top: 25, width: width < 700 ? '55%' : '68%', height: '86%'},
                hAxis: {
                    minValue: 0,
                    format: currentMeasure === 'quantity' ? '#,##0' : '£#,##0',
                    textStyle: {fontSize: 12}
                },
                vAxis: {textStyle: {fontSize: 12}},
                colors: ['#0d6efd']
            };
        }

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

    window.addEventListener('resize', function () {
        if (!chartsLoaded) return;
        window.clearTimeout(resizeTimer);
        resizeTimer = window.setTimeout(drawProductChart, 180);
    });
}());
</script>
<% End If %>
<script>
(function () {
    'use strict';

    function isBusyLink(link) {
        var href;

        if (!link || !link.href) return false;
        if (link.target && link.target.toLowerCase() !== '_self') return false;
        if (link.hasAttribute('download')) return false;
        href = link.getAttribute('href') || '';
        if (href === '' || href.charAt(0) === '#') return false;
        if (href.toLowerCase().indexOf('javascript:') === 0) return false;
        return true;
    }

    window.showBusyIndicator = function () {
        var overlay = document.getElementById('busy-overlay');
        if (!overlay) return;
        overlay.setAttribute('aria-hidden', 'false');
        document.body.classList.add('is-busy');
    };

    window.hideBusyIndicator = function () {
        var overlay = document.getElementById('busy-overlay');
        if (!overlay) return;
        document.body.classList.remove('is-busy');
        overlay.setAttribute('aria-hidden', 'true');
    };

    document.addEventListener('submit', function () {
        window.showBusyIndicator();
    }, true);

    document.addEventListener('change', function (event) {
        var el = event.target;
        var onchange;

        if (!el || !el.form) return;
        onchange = el.getAttribute('onchange') || '';
        if (onchange.indexOf('this.form.submit') !== -1) {
            window.showBusyIndicator();
        }
    }, true);

    document.addEventListener('click', function (event) {
        var link = event.target.closest ? event.target.closest('a') : null;
        if (!isBusyLink(link)) return;
        if (event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;
        window.showBusyIndicator();
    }, true);

    window.addEventListener('pageshow', function () {
        window.hideBusyIndicator();
    });
}());
</script>
<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>