Skip to content

Commit 75e225c

Browse files
committed
tools GA4
1 parent ab17f0c commit 75e225c

14 files changed

Lines changed: 1801 additions & 7 deletions

File tree

.github/workflows/hugo.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,11 @@ jobs:
5252
run: "[[ -f package-lock.json || -f npm-shrinkwrap.json ]] && npm ci || true"
5353
- name: Build CSS and JS bundles
5454
run: npm run build
55+
- name: Sync tool rankings from GA4
56+
env:
57+
GA4_PROPERTY_ID: ${{ secrets.GA4_PROPERTY_ID }}
58+
GA4_SERVICE_ACCOUNT_JSON: ${{ secrets.GA4_SERVICE_ACCOUNT_JSON }}
59+
run: npm run sync:rankings
5560
- name: Build with Hugo
5661
env:
5762
# For maximum backward compatibility with Hugo modules

README.md

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ assets/css/ Tailwind source + modern-dark-theme source
7272
| [`data/cfddc.yaml`](data/cfddc.yaml) | Optional per-year CFDDC taglines/dates |
7373
| [`data/store.yaml`](data/store.yaml) | Mobile store listings |
7474
| [`data/games.yaml`](data/games.yaml), [`data/ai.yaml`](data/ai.yaml), etc. | Hub page content |
75+
| [`data/tool-rankings.json`](data/tool-rankings.json) | GA4 click rankings for homepage Popular tools (CI-generated; `enabled: false` locally) |
7576

7677
## Adding a new hub or browse category
7778

@@ -120,7 +121,29 @@ When [`data/history.yaml`](data/history.yaml) includes dates for a new year:
120121

121122
## Deployment
122123

123-
Pushes to `main` trigger [`.github/workflows/hugo.yaml`](.github/workflows/hugo.yaml): install Hugo → `npm run build:css``hugo --minify` → deploy `public/` to GitHub Pages.
124+
Pushes to `main` trigger [`.github/workflows/hugo.yaml`](.github/workflows/hugo.yaml): install Hugo → `npm run build``npm run sync:rankings``hugo --minify` → deploy `public/` to GitHub Pages.
125+
126+
### Popular tools section (GA4)
127+
128+
The homepage can show a **Popular tools** block (`#popular-tools`) ranked by real `tool_click` analytics. It is **hidden** unless GitHub Actions successfully syncs GA4 data at build time.
129+
130+
**One-time setup:**
131+
132+
1. In GA4 Admin → **Custom definitions**, register event parameter `tool_name` as a custom dimension (events already send it from `static/js/analytics.js`).
133+
2. Copy the **numeric Property ID** from GA4 Admin → Property settings (not the `G-` measurement ID).
134+
3. In Google Cloud: enable **Google Analytics Data API**, create a service account, download JSON key, add the service account email to the GA4 property as **Viewer**.
135+
4. In the GitHub repo → **Settings → Secrets and variables → Actions**, add:
136+
- `GA4_PROPERTY_ID` — numeric property ID
137+
- `GA4_SERVICE_ACCOUNT_JSON` — full service account key JSON
138+
139+
**Behavior:**
140+
141+
- CI runs `npm run sync:rankings` before `hugo`, writing [`data/tool-rankings.json`](data/tool-rankings.json).
142+
- If secrets are missing or the API fails, `enabled` stays `false` and the section is omitted from HTML.
143+
- Local builds without secrets behave the same (committed stub has `enabled: false`).
144+
- After deploy, confirm in the Actions log: `[sync:rankings] enabled: N tools, top 5: ...`
145+
146+
Optional env: `RANKINGS_WINDOW_DAYS` (default `30`) for the GA4 lookback window.
124147

125148
## License
126149

data/tool-rankings.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"enabled": false,
3+
"source": "disabled",
4+
"reason": "not_configured",
5+
"rankings": {}
6+
}

i18n/en.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,12 @@
197197
translation: "See all →"
198198
- id: category_see_all_named
199199
translation: "See all %s (%d)"
200+
- id: popular_tools_title
201+
translation: "Popular tools"
202+
- id: popular_tools_subtitle
203+
translation: "Most clicked this month"
204+
- id: popular_tools_see_all
205+
translation: "Browse all →"
200206
- id: tool_opens_new_tab
201207
translation: "(opens in new tab)"
202208
- id: tool_default_description

i18n/fr.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,12 @@
197197
translation: "Tout voir →"
198198
- id: category_see_all_named
199199
translation: "Voir tout %s (%d)"
200+
- id: popular_tools_title
201+
translation: "Outils populaires"
202+
- id: popular_tools_subtitle
203+
translation: "Les plus cliqués ce mois-ci"
204+
- id: popular_tools_see_all
205+
translation: "Tout parcourir →"
200206
- id: tool_opens_new_tab
201207
translation: "(s'ouvre dans un nouvel onglet)"
202208
- id: tool_default_description

i18n/hi.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,12 @@
197197
translation: "सभी देखें →"
198198
- id: category_see_all_named
199199
translation: "सभी %s देखें (%d)"
200+
- id: popular_tools_title
201+
translation: "लोकप्रिय टूल"
202+
- id: popular_tools_subtitle
203+
translation: "इस महीने सबसे ज़्यादा क्लिक"
204+
- id: popular_tools_see_all
205+
translation: "सभी ब्राउज़ करें →"
200206
- id: tool_opens_new_tab
201207
translation: "(नई टैब में खुलता है)"
202208
- id: tool_default_description

i18n/ja.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,12 @@
197197
translation: "すべて見る →"
198198
- id: category_see_all_named
199199
translation: "%s をすべて見る (%d)"
200+
- id: popular_tools_title
201+
translation: "人気のツール"
202+
- id: popular_tools_subtitle
203+
translation: "今月最もクリックされたツール"
204+
- id: popular_tools_see_all
205+
translation: "すべて見る →"
200206
- id: tool_opens_new_tab
201207
translation: "(新しいタブで開く)"
202208
- id: tool_default_description

layouts/index.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222

2323
<main id="main-content" class="home-sections" tabindex="-1">
2424
{{ partial "home/hero.html" . }}
25+
{{ partial "home/popular-tools-section.html" . }}
2526
{{- $data := partial "get-data.html" . -}}
2627
{{- range $data.home.categories -}}
2728
{{ partial "home/category-section.html" (dict "category" . "Site" $.Site) }}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
{{- $data := partial "get-data.html" . -}}
2+
{{- $result := slice -}}
3+
{{- $keys := slice -}}
4+
5+
{{- range $data.home.categories -}}
6+
{{- $catName := .name -}}
7+
{{- range .items -}}
8+
{{- if ne .hidden true -}}
9+
{{- $key := partial "home/normalize-tool-url.html" .url -}}
10+
{{- if and $key (not (in $keys $key)) -}}
11+
{{- $keys = $keys | append $key -}}
12+
{{- $result = $result | append (dict "item" . "iconDir" "home" "categoryName" $catName "urlKey" $key) -}}
13+
{{- end -}}
14+
{{- end -}}
15+
{{- end -}}
16+
{{- end -}}
17+
18+
{{- $hubs := slice
19+
(dict "items" ($data.games.data | default slice) "iconDir" "games" "categoryName" (i18n "footer_games"))
20+
(dict "items" ($data.ai.data | default slice) "iconDir" "ai" "categoryName" (i18n "footer_ai"))
21+
(dict "items" ($data.designlab.data | default slice) "iconDir" "designlab" "categoryName" (i18n "footer_design"))
22+
(dict "items" ($data.store.data | default slice) "iconDir" "store" "categoryName" (i18n "footer_store"))
23+
-}}
24+
25+
{{- range $hubs -}}
26+
{{- $hubIconDir := .iconDir -}}
27+
{{- $hubCategory := .categoryName -}}
28+
{{- range .items -}}
29+
{{- if ne .hidden true -}}
30+
{{- $key := partial "home/normalize-tool-url.html" .url -}}
31+
{{- if and $key (not (in $keys $key)) -}}
32+
{{- $keys = $keys | append $key -}}
33+
{{- $result = $result | append (dict "item" . "iconDir" $hubIconDir "categoryName" $hubCategory "urlKey" $key) -}}
34+
{{- end -}}
35+
{{- end -}}
36+
{{- end -}}
37+
{{- end -}}
38+
39+
{{- return $result -}}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{{- $url := . -}}
2+
{{- $out := "" -}}
3+
{{- if or (hasPrefix $url "http://") (hasPrefix $url "https://") -}}
4+
{{- with urls.Parse $url -}}
5+
{{- $path := .Path | default "/" -}}
6+
{{- $path = strings.TrimSuffix "/" $path -}}
7+
{{- if eq $path "" -}}{{- $path = "" -}}{{- end -}}
8+
{{- $out = printf "%s://%s%s" (lower .Scheme) (lower .Host) $path -}}
9+
{{- end -}}
10+
{{- else -}}
11+
{{- $path := strings.TrimPrefix "/" (strings.TrimSuffix "/" ($url | default "")) -}}
12+
{{- if $path -}}
13+
{{- $out = printf "/%s" ($path | lower) -}}
14+
{{- else -}}
15+
{{- $out = "/" -}}
16+
{{- end -}}
17+
{{- end -}}
18+
{{- return $out -}}

0 commit comments

Comments
 (0)