From 07970244374dc145b50d1e74810d6df2d8e28cf1 Mon Sep 17 00:00:00 2001 From: Daniel Covington Date: Thu, 29 Oct 2026 13:59:40 -0400 Subject: [PATCH] Dashboard: click an outcome count to list its businesses; method split in daily activity tooltip Co-Authored-By: Claude Sonnet 5.5 --- app/controllers/BusinessesController.asp | 2 +- app/controllers/ExportController.asp | 2 +- app/lib/lib.BusinessHelpers.asp | 2 +- app/repositories/BusinessRepository.asp | 14 +++++++++++++- app/repositories/StatsRepository.asp | 22 +++++++++++++++++----- app/views/Businesses/index.asp | 7 +++++++ app/views/Dashboard/index.asp | 4 ++-- 7 files changed, 42 insertions(+), 11 deletions(-) diff --git a/app/controllers/BusinessesController.asp b/app/controllers/BusinessesController.asp index 294fe7b..cc82c69 100644 --- a/app/controllers/BusinessesController.asp +++ b/app/controllers/BusinessesController.asp @@ -32,7 +32,7 @@ Class BusinessesController_Class Dim f, k, page, pageCount, result, rows, total, industries, areas, sort, dir, firstRow, lastRow Set f = CreateObject("Scripting.Dictionary") - For Each k In Array("q", "industry", "area", "status", "show", "contacted", "phone", "sort", "dir") + For Each k In Array("q", "industry", "area", "status", "show", "contacted", "phone", "outcome", "sort", "dir") f(k) = Trim(CStr(Request.QueryString(k))) Next f("q") = Left(f("q"), 100) diff --git a/app/controllers/ExportController.asp b/app/controllers/ExportController.asp index 82df023..3b59e1a 100644 --- a/app/controllers/ExportController.asp +++ b/app/controllers/ExportController.asp @@ -31,7 +31,7 @@ Class ExportController_Class Dim f, k, rs, line, n, fileName, areaNames, bizAreas Set f = CreateObject("Scripting.Dictionary") - For Each k In Array("q", "industry", "area", "status", "show", "contacted", "phone", "sort", "dir") + For Each k In Array("q", "industry", "area", "status", "show", "contacted", "phone", "outcome", "sort", "dir") f(k) = Trim(CStr(Request.QueryString(k))) Next f("q") = Left(f("q"), 100) diff --git a/app/lib/lib.BusinessHelpers.asp b/app/lib/lib.BusinessHelpers.asp index 65c871b..4c51486 100644 --- a/app/lib/lib.BusinessHelpers.asp +++ b/app/lib/lib.BusinessHelpers.asp @@ -87,7 +87,7 @@ End Function Function Businesses_QS(f, page, sort, dir) Dim k, s s = "" - For Each k In Array("q", "industry", "area", "status", "show", "contacted", "phone") + For Each k In Array("q", "industry", "area", "status", "show", "contacted", "phone", "outcome") If f.Exists(k) Then If Len(f(k)) > 0 Then s = s & "&" & k & "=" & Server.URLEncode(f(k)) End If diff --git a/app/repositories/BusinessRepository.asp b/app/repositories/BusinessRepository.asp index e076776..cf2f827 100644 --- a/app/repositories/BusinessRepository.asp +++ b/app/repositories/BusinessRepository.asp @@ -12,6 +12,7 @@ ' show "" = everyone, "prospects", "customers" ' contacted "never" = only businesses with no contact yet ' phone "1" = only businesses with a phone number +' outcome an outcome key (or "none") = businesses with a contact logged with that outcome in the last 30 days ' sort name | city | industry | area | status | last_contacted | next_action | added (default name) ' area sorts by each business's alphabetically first area; businesses with none sort first ' dir asc | desc @@ -343,7 +344,7 @@ Class BusinessRepository_Class End Sub Private Sub BuildWhere(f) - Dim terms, t, d, q, n, frag, ind, ar, st + Dim terms, t, d, q, n, frag, ind, ar, st, oc m_where = "1=1" Set m_params = CreateObject("Scripting.Dictionary") @@ -406,6 +407,17 @@ Class BusinessRepository_Class If Val_(f, "contacted") = "never" Then m_where = m_where & " AND b.last_contacted_at IS NULL" If Val_(f, "phone") = "1" Then m_where = m_where & " AND b.phone_digits IS NOT NULL" + + ' Businesses with a contact logged in the last 30 days with this outcome (same window as the dashboard). + ' "none" = contacts logged without an outcome. Inlined from a whitelist, not a ? marker (see area above). + oc = Val_(f, "outcome") + If oc = "none" Then + m_where = m_where & " AND EXISTS (SELECT x.id FROM interactions x WHERE x.business_id = b.id " & _ + "AND x.contacted_at >= DateAdd('d', -29, Date()) AND (x.outcome IS NULL OR x.outcome = ''))" + ElseIf Businesses_IsKey(Businesses_Outcomes(), oc) Then + m_where = m_where & " AND EXISTS (SELECT x.id FROM interactions x WHERE x.business_id = b.id " & _ + "AND x.contacted_at >= DateAdd('d', -29, Date()) AND x.outcome = '" & oc & "')" + End If End Sub ' FROM clause for the list and export: the business, its industry, and fa.first_area (its alphabetically first diff --git a/app/repositories/StatsRepository.asp b/app/repositories/StatsRepository.asp index fd6d4e3..9b3e742 100644 --- a/app/repositories/StatsRepository.asp +++ b/app/repositories/StatsRepository.asp @@ -46,13 +46,21 @@ Class StatsRepository_Class Set ActivityCounts = d End Function - ' Contacts per day for the last `days` days, oldest first: list of Array(date, count) including zero days + ' Contacts per day for the last `days` days, oldest first: list of Array(date, count, methods) including zero days. + ' methods is a Dictionary of method key -> count for that day. Public Function DailyActivity(days) - Dim rs, counts, list, i, dt, key + Dim rs, counts, byMethod, list, i, dt, key, m Set counts = CreateObject("Scripting.Dictionary") - Set rs = Db_Query("SELECT contacted_at, COUNT(*) FROM interactions WHERE contacted_at >= DateAdd('d', " & CLng(1 - days) & ", Date()) GROUP BY contacted_at", Array()) + Set byMethod = CreateObject("Scripting.Dictionary") + Set rs = Db_Query("SELECT contacted_at, method, COUNT(*) FROM interactions WHERE contacted_at >= DateAdd('d', " & CLng(1 - days) & ", Date()) GROUP BY contacted_at, method", Array()) Do While Not rs.EOF - If Not IsNull(rs(0)) Then counts(CStr(CLng(CDate(rs(0))))) = CLng(rs(1)) + If Not IsNull(rs(0)) Then + key = CStr(CLng(CDate(rs(0)))) + counts(key) = N(counts(key)) + CLng(rs(2)) + If Not byMethod.Exists(key) Then Set byMethod(key) = CreateObject("Scripting.Dictionary") + m = rs(1) & "" + byMethod(key)(m) = N(byMethod(key)(m)) + CLng(rs(2)) + End If rs.MoveNext Loop rs.Close @@ -60,7 +68,11 @@ Class StatsRepository_Class For i = days - 1 To 0 Step -1 dt = Date - i key = CStr(CLng(dt)) - If counts.Exists(key) Then list.Add list.Count, Array(dt, counts(key)) Else list.Add list.Count, Array(dt, 0) + If counts.Exists(key) Then + list.Add list.Count, Array(dt, counts(key), byMethod(key)) + Else + list.Add list.Count, Array(dt, 0, CreateObject("Scripting.Dictionary")) + End If Next Set DailyActivity = list End Function diff --git a/app/views/Businesses/index.asp b/app/views/Businesses/index.asp index 5ed779a..ee1be1f 100644 --- a/app/views/Businesses/index.asp +++ b/app/views/Businesses/index.asp @@ -66,6 +66,13 @@ + <% If Len(f("outcome")) > 0 Then %> + " /> +
+ Outcome: <% If f("outcome") = "none" Then Response.Write "(no outcome)" Else Response.Write H(Businesses_Label(Businesses_Outcomes(), f("outcome"))) %> · last 30 days + clear +
+ <% End If %>
Reset diff --git a/app/views/Dashboard/index.asp b/app/views/Dashboard/index.asp index 5ed7bf2..3fe4598 100644 --- a/app/views/Dashboard/index.asp +++ b/app/views/Dashboard/index.asp @@ -88,7 +88,7 @@ tiles = Array( _ dd = daily(di) barPct = Round(dd(1) * 100 / maxDaily, 0) If dd(1) > 0 And barPct < 6 Then barPct = 6 %> -
+
<% Next %>">
<% If dd(1) > 0 Then Response.Write dd(1) %>
" style="height: <%= barPct %>%; min-height: <% If dd(1) > 0 Then Response.Write "2px" Else Response.Write "1px" %>;<% If dd(1) = 0 Then Response.Write " background:#dee2e6;" %>">
@@ -130,7 +130,7 @@ tiles = Array( _ ob = byOutcome(oi) %> <% If Len(ob(0)) = 0 Then Response.Write "(no outcome)" Else Response.Write H(Businesses_Label(Businesses_Outcomes(), ob(0))) %> - <%= FormatNumber(ob(1), 0) %> + " class="text-decoration-none" title="List these businesses"><%= FormatNumber(ob(1), 0) %> <% Next %>