From 3fbba0c2f03f4b5c92f70a6c74b689590a1d2767 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?P=C4=93teris=20Caune?= <cuu508@gmail.com>
Date: Tue, 13 Aug 2024 13:57:52 +0300
Subject: [PATCH] Update timezone dropdowns to show frequently used timezones
 at the top

---
 CHANGELOG.md                             |  1 +
 hc/front/views.py                        | 17 ++++++++++++++---
 static/js/initialize-timezone-selects.js | 13 +++++++++++--
 templates/front/checks.html              |  3 ++-
 templates/front/details.html             |  3 ++-
 5 files changed, 30 insertions(+), 7 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 89556217..8793e458 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,7 @@ All notable changes to this project will be documented in this file.
 - Improve performance of loading ping body previews (#1023)
 - Implement MS Teams Workflows integration (#1024)
 - Add "uuid" field in API responses when read/write key is used (#1007)
+- Update timezone dropdowns to show frequently used timezones at the top
 
 ### Bug Fixes
 - Fix Check.ping() to lock the check before updating (#1023)
diff --git a/hc/front/views.py b/hc/front/views.py
index 9ef6f284..1402cd4a 100644
--- a/hc/front/views.py
+++ b/hc/front/views.py
@@ -108,6 +108,14 @@ def _tags_counts(checks: Iterable[Check]) -> tuple[list[tuple[str, str, str]], i
     return result, num_down
 
 
+def _common_timezones(checks: Iterable[Check]) -> list[str]:
+    counter: Counter[str] = Counter()
+    for check in checks:
+        counter[check.tz] += 1
+
+    return [tz for tz, _ in counter.most_common(3)]
+
+
 def _get_check_for_user(
     request: HttpRequest, code: UUID, preload_owner_profile: bool = False
 ) -> tuple[Check, bool]:
@@ -280,6 +288,7 @@ def checks(request: AuthenticatedHttpRequest, code: UUID) -> HttpResponse:
         "num_down": num_down,
         "tags": tags_counts,
         "ping_endpoint": settings.PING_ENDPOINT,
+        "common_timezones": _common_timezones(checks),
         "timezones": all_timezones,
         "project": project,
         "num_available": project.num_checks_available(),
@@ -976,9 +985,10 @@ def details(request: AuthenticatedHttpRequest, code: UUID) -> HttpResponse:
         channels.append(channel)
 
     all_tags = set()
-    q = Check.objects.filter(project=check.project).exclude(tags="")
-    for tags in q.values_list("tags", flat=True):
-        all_tags.update(tags.split(" "))
+    sibling_checks = Check.objects.filter(project=check.project).only("tags", "tz")
+    for sibling in sibling_checks:
+        if sibling.tags:
+            all_tags.update(sibling.tags.split(" "))
 
     ctx = {
         "page": "details",
@@ -988,6 +998,7 @@ def details(request: AuthenticatedHttpRequest, code: UUID) -> HttpResponse:
         "channels": regular_channels,
         "group_channels": group_channels,
         "enabled_channels": list(check.channel_set.all()),
+        "common_timezones": _common_timezones(sibling_checks),
         "timezones": all_timezones,
         "downtimes": check.downtimes(3, request.profile.tz),
         "tz": request.profile.tz,
diff --git a/static/js/initialize-timezone-selects.js b/static/js/initialize-timezone-selects.js
index 5a3a46c2..584c7887 100644
--- a/static/js/initialize-timezone-selects.js
+++ b/static/js/initialize-timezone-selects.js
@@ -1,8 +1,17 @@
 $(function() {
-    var timezones = document.getElementById("all-timezones").textContent;
+    function makeOptions(domId) {
+        var s = document.getElementById(domId).textContent;
+        return s.split(",").map(tz => ({value: tz, group: domId}))
+    }
+
     $("select[name=tz]").selectize({
         labelField: "value",
         searchField: ["value"],
-        options: timezones.split(",").map(tz => ({value: tz}))
+        options: makeOptions("common-timezones").concat(makeOptions("all-timezones")),
+        optgroups: [
+            {label: "Common time zones", value: "common-timezones"},
+            {label: "All time zones (search by typing)", value: "all-timezones"}
+        ],
+        optgroupField: "group"
     });
 });
\ No newline at end of file
diff --git a/templates/front/checks.html b/templates/front/checks.html
index fa433383..4db4e575 100644
--- a/templates/front/checks.html
+++ b/templates/front/checks.html
@@ -145,7 +145,8 @@
 {% endblock %}
 
 {% block scripts %}
-<script id="all-timezones" type="data">UTC,{{ timezones|join:"," }}</script>
+<script id="common-timezones" type="data">{{ common_timezones|join:"," }}</script>
+<script id="all-timezones" type="data">{{ timezones|join:"," }}</script>
 {% compress js %}
 <script src="{% static 'js/selectize.min.js' %}"></script>
 <script src="{% static 'js/nouislider.min.js' %}"></script>
diff --git a/templates/front/details.html b/templates/front/details.html
index a4a18907..db134b26 100644
--- a/templates/front/details.html
+++ b/templates/front/details.html
@@ -386,7 +386,8 @@
 {% endblock %}
 
 {% block scripts %}
-<script id="all-timezones" type="data">UTC,{{ timezones|join:"," }}</script>
+<script id="common-timezones" type="data">{{ common_timezones|join:"," }}</script>
+<script id="all-timezones" type="data">{{ timezones|join:"," }}</script>
 {% compress js %}
 <script src="{% static 'js/selectize.min.js' %}"></script>
 <script src="{% static 'js/nouislider.min.js' %}"></script>