ultimate-jekyll-manager 1.9.5 → 1.9.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/dist/assets/css/core/_initialize.scss +0 -11
  2. package/dist/assets/css/core/_utilities.scss +5 -2
  3. package/dist/assets/js/libs/form-manager.js +5 -10
  4. package/dist/defaults/_.gitignore +4 -0
  5. package/dist/defaults/dist/_includes/core/body.html +1 -7
  6. package/package.json +8 -1
  7. package/.nvmrc +0 -1
  8. package/CHANGELOG.md +0 -804
  9. package/CLAUDE-ATTRIBUTION.md +0 -215
  10. package/PROGRESS.md +0 -36
  11. package/TODO-ATTRIBUTION2.md +0 -753
  12. package/TODO-AUTH-TESTING.md +0 -165
  13. package/TODO-BACKEND.md +0 -13
  14. package/TODO-CHAT1.md +0 -41
  15. package/TODO-CONSENT-LIVETEST.md +0 -201
  16. package/TODO-NEW.md +0 -21
  17. package/TODO-themes.md +0 -34
  18. package/TODO-tracking.md +0 -69
  19. package/TODO-tracking2.md +0 -103
  20. package/TODO.md +0 -415
  21. package/_notes/CLAUDE-pages.md +0 -19
  22. package/_notes/CLAUDE-pages2.md +0 -18
  23. package/_notes/LOGOS.md +0 -1
  24. package/_notes/NOTES-app.md +0 -92
  25. package/_notes/NOTES-sub-2.md +0 -42
  26. package/_notes/NOTES-sub-ai.md +0 -165
  27. package/_notes/NOTES-sub-endpoints.md +0 -44
  28. package/_notes/NOTES-sub-example.md +0 -92
  29. package/_notes/NOTES-sub-plan.md +0 -36
  30. package/_notes/NOTES-sub-status.md +0 -123
  31. package/_notes/TODO-Claude.rtf +0 -105
  32. package/_notes/TODO-FileStructure.md +0 -249
  33. package/_notes/TODO-checkout.md +0 -33
  34. package/_notes/TODO-frontend.md +0 -61
  35. package/_notes/TODO-legacy.md +0 -18
  36. package/_notes/TODO-template-system.md +0 -73
  37. package/_notes/TODO-theme.md +0 -17
  38. package/logs/test.log +0 -144
  39. package/plans/fix-translation-exclude-404s.md +0 -113
@@ -1,215 +0,0 @@
1
- # Attribution Tracking System — Design & Implementation Plan
2
-
3
- > Working design doc for the unified attribution system. Captures UTM/ITM tags, affiliate codes, and ad click IDs with a per-type "first-touch with 30-day refresh" model, plus auto-sync to the server when authenticated.
4
-
5
- ## Goals
6
-
7
- An all-in-one system to track how users arrive at the site so we can:
8
- 1. Do UTM tracking (external marketing attribution)
9
- 2. Do ITM tracking (internal mechanisms — exit popups, extension prompts, etc.)
10
- 3. Capture ad click IDs for server-side conversion events (GA, Meta CAPI, TikTok Events API)
11
- 4. Properly attribute recurring purchases that happen offline (subscription renewals)
12
-
13
- ---
14
-
15
- ## Design Decisions (settled)
16
-
17
- - **Attribution Model**: First-touch with 30-day refresh, **per-type basis**
18
- - Each category (UTM, ITM, affiliate, adClicks) has independent freshness
19
- - If no data exists for a type → save it
20
- - If data exists AND is < 30 days old → preserve (first-touch protection)
21
- - If data exists AND is >= 30 days old → overwrite (new journey)
22
- - Categories are independent: can overwrite stale UTM while keeping fresh affiliate
23
- - **Freshness TTL**: 30 days globally for all types
24
- - **Ad click IDs**: lumped into a single `adClicks` object (user only arrives from one ad at a time; they share one timestamp)
25
- - **Server Sync**: on attribution change (if signed in) + signup + checkout
26
- - `_meta.needsSync` flag for deferred sync when user later signs in (mirrors `notifications.syncSubscription()` pattern in web-manager)
27
- - **Storage**: User doc (rolling latest) + Subscription doc (frozen snapshot at purchase)
28
- - **`getFresh()` returns ALL fresh data** — server decides what's recent enough to attribute. Subscription gets a frozen snapshot used for ALL recurring conversions, even years later.
29
- - **No backwards compatibility** — replace the old shape cleanly, remove the ad-hoc 30-day check.
30
-
31
- ---
32
-
33
- ## Storage Structure
34
-
35
- ```javascript
36
- // localStorage key: "attribution"
37
- {
38
- utm: {
39
- tags: { utm_source, utm_medium, utm_campaign, utm_term, utm_content },
40
- timestamp: "ISO string",
41
- url: "full landing URL",
42
- page: "/path"
43
- },
44
- itm: {
45
- tags: { itm_source, itm_medium, itm_campaign, itm_content },
46
- timestamp: "ISO string",
47
- url: "full URL",
48
- page: "/path"
49
- },
50
- affiliate: {
51
- code: "partner123",
52
- timestamp: "ISO string",
53
- url: "full URL",
54
- page: "/path"
55
- },
56
- adClicks: {
57
- // All ad click IDs lumped together (user only arrives from one ad at a time)
58
- fbclid: "from URL param",
59
- fbc: "from _fbc cookie",
60
- gclid: "from URL param",
61
- ttclid: "from URL param",
62
- timestamp: "ISO string",
63
- url: "full URL",
64
- page: "/path"
65
- },
66
- _meta: {
67
- needsSync: false, // true if attribution changed while signed out
68
- lastSynced: "ISO string" // last successful server sync
69
- }
70
- }
71
- ```
72
-
73
- ---
74
-
75
- ## Data Captured
76
-
77
- | Category | Source | Params |
78
- |---|---|---|
79
- | **UTM** | URL query | `utm_source`, `utm_medium`, `utm_campaign`, `utm_term`, `utm_content` |
80
- | **ITM** | URL query (internal links, e.g. exit popup) | `itm_source`, `itm_medium`, `itm_campaign`, `itm_content` |
81
- | **Affiliate** | URL query | `aff` or `ref` |
82
- | **Ad Clicks** | URL query + cookie | `fbclid`, `fbc` (`_fbc` cookie), `gclid`, `ttclid` |
83
-
84
- ITM tags are emitted by internal mechanisms. Example: `exit-popup.js` already sets `itm_source=website&itm_medium=modal&itm_campaign=exit-popup&itm_content=<pathname>` on its CTA link. Those land as a query string on the next page and get captured like UTM.
85
-
86
- ---
87
-
88
- ## Files to Modify
89
-
90
- ### 1. `src/assets/js/core/query-strings.js` — Core refactor
91
-
92
- Currently captures UTM + affiliate only (no freshness, no ITM, no ad clicks, no sync). Becomes the single owner of attribution capture + sync.
93
-
94
- Add:
95
- 1. Constants: `ATTRIBUTION_KEY = 'attribution'`, `FRESHNESS_DAYS = 30`, `FRESHNESS_MS = 30 * 24 * 60 * 60 * 1000`
96
- 2. `shouldPreserveAttribution(existingData)` — returns `true` if existing data is < 30 days old (preserve), `false` if missing or stale (allow overwrite)
97
- 3. `processUTMParams()` / `processITMParams()` / `processAffiliateParams()` / `processAdClickParams()` — each:
98
- - Reads its params, quits if none present
99
- - Applies `shouldPreserveAttribution()` first-touch check
100
- - Writes its category + metadata (timestamp/url/page)
101
- - Returns `true` if it changed anything
102
- 4. `getFbcCookie()` — parse `document.cookie` for `_fbc`
103
- 5. `getAttribution()` — raw stored object (all, regardless of age)
104
- 6. `getFreshAttribution()` — only categories < 30 days old, excludes `_meta`. This is the canonical thing sent to the server.
105
- 7. `syncAttribution()`:
106
- - If user signed in → POST fresh attribution to backend-manager, set `_meta.lastSynced`, clear `_meta.needsSync`
107
- - If not signed in → set `_meta.needsSync = true`
108
- 8. Auth-state listener for deferred sync: on sign-in, if `_meta.needsSync` is true, call `syncAttribution()`
109
- 9. Public API on `webManager._ujLibrary.attribution`:
110
- ```javascript
111
- {
112
- get: () => getAttribution(), // raw, all ages — debugging
113
- getFresh: () => getFreshAttribution(), // < 30 days — USE THIS
114
- sync: () => syncAttribution(), // manual sync (usually automatic)
115
- clear: () => clearAttribution() // wipe all attribution
116
- }
117
- ```
118
-
119
- `processQueryStrings()` orchestrates: run each processor, OR their return values; if anything changed, save to storage and call `syncAttribution()`.
120
-
121
- ### 2. `src/assets/js/core/auth.js` — Use fresh attribution in signup
122
-
123
- `sendUserSignupMetadata(account)` currently reads raw `webManager.storage().get('attribution', {})` (line ~283). Change to:
124
- ```javascript
125
- const attribution = webManager.uj().attribution.getFresh();
126
- ```
127
- Everything else (the `flags.signupProcessed` SSOT gate, `/backend-manager/user/signup` endpoint, consent payload) stays as-is. Sync-on-change in query-strings.js is complementary — signup still sends attribution + context + consent in one shot.
128
-
129
- ### 3. `src/assets/js/pages/payment/checkout/modules/api.js` — Use fresh attribution
130
-
131
- Line ~60 currently sends raw storage:
132
- ```javascript
133
- attribution: webManager.storage().get('attribution', {}),
134
- ```
135
- Change to:
136
- ```javascript
137
- attribution: webManager.uj().attribution.getFresh(),
138
- ```
139
- (The old ad-hoc 30-day UTM check from the former `session.js` is already gone — this just swaps raw → fresh so stale categories are dropped before the server snapshots them onto the subscription.)
140
-
141
- ---
142
-
143
- ## Sync Flow
144
-
145
- ```
146
- User lands with ?utm_source=google (or itm_/fbclid/etc.)
147
-
148
-
149
- processQueryStrings()
150
- - per-type freshness check (first-touch w/ 30-day refresh)
151
- - update localStorage
152
- - did anything change?
153
- │ yes
154
-
155
- Is user signed in?
156
- ├── YES → syncAttribution() now → set _meta.lastSynced
157
- └── NO → set _meta.needsSync = true
158
-
159
- ▼ (later) user signs in
160
- auth-state listener
161
- - _meta.needsSync? → syncAttribution() → clear flag
162
- ```
163
-
164
- ---
165
-
166
- ## Server-Side Data Flow (informational — backend out of scope here)
167
-
168
- ```
169
- USER DOC /users/{uid}
170
- attribution: { utm, itm, affiliate, adClicks } ← rolling update on each sync
171
-
172
- │ on purchase, snapshot →
173
-
174
- SUBSCRIPTION DOC /users/{uid}/subscriptions/{subId}
175
- attribution: { ... } ← FROZEN at purchase time
176
- → used for ALL recurring billing conversion events (even years later)
177
- ```
178
-
179
- When a renewal fires months later, the server reads the subscription's frozen `attribution.adClicks.fbclid` (or gclid/ttclid) to send a server-side conversion event, attributing the recurring revenue to the original campaign.
180
-
181
- ---
182
-
183
- ## Public API Reference
184
-
185
- ```javascript
186
- webManager.uj().attribution.get() // raw stored attribution (all ages)
187
- webManager.uj().attribution.getFresh() // only < 30 days — send this to server
188
- webManager.uj().attribution.sync() // manual sync (normally automatic)
189
- webManager.uj().attribution.clear() // wipe all attribution
190
- ```
191
-
192
- ---
193
-
194
- ## Testing Checklist
195
-
196
- - [ ] UTM params captured on landing
197
- - [ ] ITM params captured (via exit-popup CTA link)
198
- - [ ] Affiliate code captured (`aff` / `ref`)
199
- - [ ] Ad click IDs captured (`fbclid`, `gclid`, `ttclid`)
200
- - [ ] `_fbc` cookie read correctly
201
- - [ ] First-touch protected per-type (revisit doesn't overwrite fresh data)
202
- - [ ] 30-day refresh per-type (stale category overwritten, fresh ones kept)
203
- - [ ] `getFresh()` excludes stale categories and `_meta`
204
- - [ ] Sync fires when signed in + attribution changes
205
- - [ ] `_meta.needsSync` set when signed out + attribution changes
206
- - [ ] Deferred sync fires on sign-in when `needsSync` is true
207
- - [ ] Signup (`auth.js`) sends `getFresh()` attribution
208
- - [ ] Checkout (`api.js`) sends `getFresh()` attribution
209
-
210
- ---
211
-
212
- ## Open Questions / Notes
213
-
214
- - Confirm the backend `user/signup` and checkout endpoints accept the new nested `adClicks` / `itm` shape (no backwards-compat shim — server should be updated in lockstep).
215
- - Decide the sync command/endpoint for the standalone `syncAttribution()` call (e.g. `user/attribution` vs reusing signup). Signup already carries attribution, so sync-on-change is mainly for users who change attribution *after* signup but *before* checkout.
package/PROGRESS.md DELETED
@@ -1,36 +0,0 @@
1
- # Project Progress Tracker
2
- > Agents and maintainers should update this file regularly to reflect the current state of the project.
3
-
4
- ## Current Focus
5
- * **Goal:** FormManager disabled-state refactor (snapshot-and-restore)
6
- * **Current Phase:** Phase 1 — implementation + tests complete, docs pending
7
- * **Priority:** Medium
8
- * **Last Updated:** 2026-06-20 11:03 PM PDT
9
- * **Notes:** FM disabled-state refactor done + comprehensive FM test suite added (110 tests total, up from 80). Blog post ad insertion fix applied (Phase 2). Auth form HTML fixed to remove `disabled` from inputs (loading guards that conflicted with FM snapshot). Docs (javascript-libraries.md, CHANGELOG) still need updating before shipping.
10
-
11
- ## Active Task List
12
- * [ ] Phase 1: FormManager disabled-state snapshot-and-restore
13
- * [x] Task 1.1: Refactor `_setDisabled` to use snapshot instead of `data-fm-keep-disabled`
14
- * [x] Task 1.2: Add `_permanentlyDisabled` Set, populated in `_init()` before first disable
15
- * [x] Task 1.3: Write page-layer tests (5 tests: snapshot capture, full disable, selective re-enable, cycle durability, onsubmit HTML guard)
16
- * [x] Task 1.4: All 85 tests passing
17
- * [x] Task 1.5: Add visual Test 7 to FM test page (form-manager.html + JS) with permanently disabled fields + rapid-cycle demo
18
- * [x] Task 1.6: Write comprehensive FM page-layer tests — getData/setData (12 tests), validation/honeypot/file-accept (13 tests). 110 total.
19
- * [x] Task 1.7a: Full audit + fix: remove `disabled` from all loading-guard inputs across UJM forms (auth signin/signup/reset, email-preferences, hero-demo-form input/select/textarea)
20
- * [x] Task 1.7b: Add `data-form-state="initializing"` to all 34 FM-managed forms + 7 test forms, CSS guard in `_initialize.scss`, FM sets attribute in `_init()`. Remove loading-guard `disabled` from test form inputs (tests 1-6).
21
- * [ ] Task 1.7: Update `docs/javascript-libraries.md` — replace `data-fm-keep-disabled` docs with new snapshot pattern + `onsubmit="return false"` + `data-form-state="initializing"` CSS guard
22
- * [ ] Task 1.8: Update CHANGELOG with the change
23
- * [ ] Task 1.9: Ship (commit, push, publish)
24
-
25
- * [ ] Phase 3: Fix translation exclude list not respected by footer language switcher
26
- * [x] Task 3.1: Diagnose root cause — footer renders dead `/es/blog/...` links for excluded pages
27
- * [x] Task 3.2: Fix footer template (`classy/frontend/sections/footer.html`) to check exclude list
28
- * [x] Task 3.3: Fix `uj_translation_url` Ruby tag in `jekyll-uj-powertools` gem v1.7.11 (defense-in-depth)
29
- * [ ] Task 3.4: Validate on a consumer site (rebuild + check footer on blog post)
30
- * [ ] Task 3.5: Ship both repos (commit, push, publish gem + UJM)
31
-
32
- * [x] Phase 2: Fix blog post ad insertion inside blockquotes
33
- * [x] Task 2.1: Filter out `<p>` elements inside `<blockquote>`, `<details>`, `<figure>` from ad insertion candidates in `post.js`
34
-
35
- ## Completed Task List
36
- * [x] Phase 0: v1.9.0 release — MCP OAuth flow + CDP debugging docs + dev-URL updates