fragment-headless-sdk 2.2.1 → 2.3.1
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.
- package/dist/utils/attribution.d.ts +32 -0
- package/dist/utils/attribution.js +89 -0
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.js +1 -0
- package/dist/utils/metrics.d.ts +4 -0
- package/dist/utils/metrics.js +30 -1
- package/docs/CHANGELOG.md +542 -0
- package/package.json +3 -2
- package/readme.md +110 -2
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { SectionType } from '../constants';
|
|
2
|
+
export interface FragmentAttribution {
|
|
3
|
+
sectionId: string;
|
|
4
|
+
sectionType: SectionType;
|
|
5
|
+
timestamp: number;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Store attribution data for a section click
|
|
9
|
+
* Uses sessionStorage for session-scoped attribution (last-click wins)
|
|
10
|
+
* @param sectionId - The UUID of the section
|
|
11
|
+
* @param sectionType - The type of section (announcement or hero_banner)
|
|
12
|
+
*/
|
|
13
|
+
export declare function setAttribution(sectionId: string, sectionType: SectionType): void;
|
|
14
|
+
/**
|
|
15
|
+
* Retrieve stored attribution data
|
|
16
|
+
* @returns The stored attribution or null if none exists or data is invalid
|
|
17
|
+
*/
|
|
18
|
+
export declare function getAttribution(): FragmentAttribution | null;
|
|
19
|
+
/**
|
|
20
|
+
* Clear stored attribution data
|
|
21
|
+
* Called after successful conversion tracking
|
|
22
|
+
*/
|
|
23
|
+
export declare function clearAttribution(): void;
|
|
24
|
+
declare global {
|
|
25
|
+
interface Window {
|
|
26
|
+
fragmentAttribution?: {
|
|
27
|
+
get: () => FragmentAttribution | null;
|
|
28
|
+
set: (sectionId: string, sectionType: SectionType) => void;
|
|
29
|
+
clear: () => void;
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { SectionType } from '../constants';
|
|
2
|
+
const STORAGE_KEY = 'fragment_attribution';
|
|
3
|
+
/**
|
|
4
|
+
* Store attribution data for a section click
|
|
5
|
+
* Uses sessionStorage for session-scoped attribution (last-click wins)
|
|
6
|
+
* @param sectionId - The UUID of the section
|
|
7
|
+
* @param sectionType - The type of section (announcement or hero_banner)
|
|
8
|
+
*/
|
|
9
|
+
export function setAttribution(sectionId, sectionType) {
|
|
10
|
+
if (typeof sessionStorage === 'undefined')
|
|
11
|
+
return;
|
|
12
|
+
const attribution = {
|
|
13
|
+
sectionId,
|
|
14
|
+
sectionType,
|
|
15
|
+
timestamp: Date.now()
|
|
16
|
+
};
|
|
17
|
+
try {
|
|
18
|
+
sessionStorage.setItem(STORAGE_KEY, JSON.stringify(attribution));
|
|
19
|
+
}
|
|
20
|
+
catch (e) {
|
|
21
|
+
// Handle quota exceeded or other storage errors silently
|
|
22
|
+
console.warn('Fragment: Failed to set attribution', e);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Validates that parsed data matches the FragmentAttribution interface
|
|
27
|
+
*/
|
|
28
|
+
function isValidAttribution(data) {
|
|
29
|
+
if (!data || typeof data !== 'object')
|
|
30
|
+
return false;
|
|
31
|
+
const obj = data;
|
|
32
|
+
return (typeof obj.sectionId === 'string' &&
|
|
33
|
+
(obj.sectionType === SectionType.Announcement || obj.sectionType === SectionType.HeroBanner) &&
|
|
34
|
+
typeof obj.timestamp === 'number');
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Retrieve stored attribution data
|
|
38
|
+
* @returns The stored attribution or null if none exists or data is invalid
|
|
39
|
+
*/
|
|
40
|
+
export function getAttribution() {
|
|
41
|
+
if (typeof sessionStorage === 'undefined')
|
|
42
|
+
return null;
|
|
43
|
+
try {
|
|
44
|
+
const stored = sessionStorage.getItem(STORAGE_KEY);
|
|
45
|
+
if (!stored)
|
|
46
|
+
return null;
|
|
47
|
+
const parsed = JSON.parse(stored);
|
|
48
|
+
if (!isValidAttribution(parsed)) {
|
|
49
|
+
// Invalid data - clear it and return null
|
|
50
|
+
sessionStorage.removeItem(STORAGE_KEY);
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
return parsed;
|
|
54
|
+
}
|
|
55
|
+
catch (e) {
|
|
56
|
+
// Handle parse errors or other issues
|
|
57
|
+
console.warn('Fragment: Failed to get attribution', e);
|
|
58
|
+
// Clear potentially corrupted data
|
|
59
|
+
try {
|
|
60
|
+
sessionStorage.removeItem(STORAGE_KEY);
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
// Ignore errors when clearing
|
|
64
|
+
}
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Clear stored attribution data
|
|
70
|
+
* Called after successful conversion tracking
|
|
71
|
+
*/
|
|
72
|
+
export function clearAttribution() {
|
|
73
|
+
if (typeof sessionStorage === 'undefined')
|
|
74
|
+
return;
|
|
75
|
+
try {
|
|
76
|
+
sessionStorage.removeItem(STORAGE_KEY);
|
|
77
|
+
}
|
|
78
|
+
catch (e) {
|
|
79
|
+
console.warn('Fragment: Failed to clear attribution', e);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
// Make globally accessible for Shopify theme integrations
|
|
83
|
+
if (typeof window !== 'undefined') {
|
|
84
|
+
window.fragmentAttribution = {
|
|
85
|
+
get: getAttribution,
|
|
86
|
+
set: setAttribution,
|
|
87
|
+
clear: clearAttribution
|
|
88
|
+
};
|
|
89
|
+
}
|
package/dist/utils/index.d.ts
CHANGED
package/dist/utils/index.js
CHANGED
package/dist/utils/metrics.d.ts
CHANGED
|
@@ -8,4 +8,8 @@ declare global {
|
|
|
8
8
|
dataLayer?: unknown[];
|
|
9
9
|
}
|
|
10
10
|
}
|
|
11
|
+
/**
|
|
12
|
+
* Fire a scroll past metric when user scrolls past a section
|
|
13
|
+
*/
|
|
14
|
+
export declare function fireScrollPastMetric(measurementId: string | undefined, sectionType: SectionType, sectionId: string): void;
|
|
11
15
|
export declare function fireImpressionWhenVisible(el: HTMLElement, pixelUrl: string, measurementId?: string, sectionType?: SectionType, sectionId?: string): void;
|
package/dist/utils/metrics.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { SectionType } from "../constants";
|
|
2
|
+
import { setAttribution } from "./attribution";
|
|
2
3
|
// --- Unicode-safe Base64URL ---
|
|
3
4
|
export function toBase64Url(input) {
|
|
4
5
|
// Handles emojis & non-ASCII reliably:
|
|
@@ -22,6 +23,10 @@ export function fireClickMetric(clickUrl, measurementId, sectionType, sectionId)
|
|
|
22
23
|
return;
|
|
23
24
|
if (!clickUrl)
|
|
24
25
|
return;
|
|
26
|
+
// Store attribution for potential purchase/add-to-cart
|
|
27
|
+
if (sectionType && sectionId) {
|
|
28
|
+
setAttribution(sectionId, sectionType);
|
|
29
|
+
}
|
|
25
30
|
// Send to GA4 first (if available)
|
|
26
31
|
if (measurementId && sectionType && sectionId) {
|
|
27
32
|
sendGA4Event("click", measurementId, sectionType, sectionId);
|
|
@@ -65,7 +70,7 @@ function getSectionEventName(baseEventName, sectionType) {
|
|
|
65
70
|
* Only sends section_id parameter - event name already indicates section type.
|
|
66
71
|
* This is a fire-and-forget operation that won't throw errors.
|
|
67
72
|
*/
|
|
68
|
-
function sendGA4Event(baseEventName, measurementId, sectionType, sectionId) {
|
|
73
|
+
function sendGA4Event(baseEventName, measurementId, sectionType, sectionId, additionalParams) {
|
|
69
74
|
if (typeof window === "undefined")
|
|
70
75
|
return;
|
|
71
76
|
if (!measurementId)
|
|
@@ -76,12 +81,25 @@ function sendGA4Event(baseEventName, measurementId, sectionType, sectionId) {
|
|
|
76
81
|
const eventName = getSectionEventName(baseEventName, sectionType);
|
|
77
82
|
window.gtag("event", eventName, {
|
|
78
83
|
section_id: sectionId,
|
|
84
|
+
...additionalParams,
|
|
79
85
|
});
|
|
80
86
|
}
|
|
81
87
|
catch {
|
|
82
88
|
// Silently fail - don't break tracking if GA4 fails
|
|
83
89
|
}
|
|
84
90
|
}
|
|
91
|
+
/**
|
|
92
|
+
* Fire a scroll past metric when user scrolls past a section
|
|
93
|
+
*/
|
|
94
|
+
export function fireScrollPastMetric(measurementId, sectionType, sectionId) {
|
|
95
|
+
if (typeof window === "undefined")
|
|
96
|
+
return;
|
|
97
|
+
if (!measurementId || !sectionType || !sectionId)
|
|
98
|
+
return;
|
|
99
|
+
sendGA4Event("scroll_past", measurementId, sectionType, sectionId, {
|
|
100
|
+
engagement_type: "scroll_past",
|
|
101
|
+
});
|
|
102
|
+
}
|
|
85
103
|
// --- View tracking (once per element) ---
|
|
86
104
|
const seenEls = typeof WeakSet !== "undefined" ? new WeakSet() : null;
|
|
87
105
|
export function fireImpressionWhenVisible(el, pixelUrl, measurementId, sectionType, sectionId) {
|
|
@@ -92,6 +110,7 @@ export function fireImpressionWhenVisible(el, pixelUrl, measurementId, sectionTy
|
|
|
92
110
|
if (seenEls && seenEls.has(el))
|
|
93
111
|
return; // de-dupe by element
|
|
94
112
|
let fired = false;
|
|
113
|
+
let hasScrolledPast = false;
|
|
95
114
|
const img = new Image();
|
|
96
115
|
const fire = () => {
|
|
97
116
|
if (fired)
|
|
@@ -119,6 +138,16 @@ export function fireImpressionWhenVisible(el, pixelUrl, measurementId, sectionTy
|
|
|
119
138
|
for (const e of entries) {
|
|
120
139
|
if (e.isIntersecting && e.intersectionRatio >= 0.3) {
|
|
121
140
|
fire();
|
|
141
|
+
}
|
|
142
|
+
else if (!e.isIntersecting &&
|
|
143
|
+
!hasScrolledPast &&
|
|
144
|
+
e.boundingClientRect.top < 0 &&
|
|
145
|
+
fired) {
|
|
146
|
+
// User scrolled past the section (it's above viewport and was previously visible)
|
|
147
|
+
hasScrolledPast = true;
|
|
148
|
+
if (measurementId && sectionType && sectionId) {
|
|
149
|
+
fireScrollPastMetric(measurementId, sectionType, sectionId);
|
|
150
|
+
}
|
|
122
151
|
io.disconnect();
|
|
123
152
|
break;
|
|
124
153
|
}
|
|
@@ -0,0 +1,542 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
### [2.3.1] - 2026-01-25
|
|
9
|
+
|
|
10
|
+
### 🔧 Technical Improvements
|
|
11
|
+
|
|
12
|
+
- **Enhanced Error Handling** – Improved error handling in attribution functions
|
|
13
|
+
- Better error messages and logging
|
|
14
|
+
- Automatic cleanup of corrupted data
|
|
15
|
+
- Graceful degradation when sessionStorage is unavailable
|
|
16
|
+
|
|
17
|
+
## [2.3.0] - 2026-01-25
|
|
18
|
+
|
|
19
|
+
### ✨ Attribution Tracking System
|
|
20
|
+
|
|
21
|
+
- **Session-Based Attribution** – New attribution tracking system for purchase/add-to-cart attribution
|
|
22
|
+
- Automatically stores attribution data when users click section buttons
|
|
23
|
+
- Uses sessionStorage for session-scoped attribution (last-click wins)
|
|
24
|
+
- Tracks section ID, section type, and timestamp for each click
|
|
25
|
+
|
|
26
|
+
### 📚 Documentation
|
|
27
|
+
|
|
28
|
+
- **New Attribution Section** – Added comprehensive documentation for attribution tracking
|
|
29
|
+
- Usage examples for Shopify theme integrations
|
|
30
|
+
- Programmatic access examples
|
|
31
|
+
- Use cases for purchase and add-to-cart attribution
|
|
32
|
+
|
|
33
|
+
## [2.2.0] - 2025-01-18
|
|
34
|
+
|
|
35
|
+
### ✨ Google Analytics 4 (GA4) Integration
|
|
36
|
+
|
|
37
|
+
- **GA4 Event Tracking** – Added comprehensive Google Analytics 4 integration for section tracking
|
|
38
|
+
- Automatic `section_view` events when sections become visible
|
|
39
|
+
- Automatic `section_click` events when users interact with buttons
|
|
40
|
+
- Configurable via `measurementId`, `sectionId`, and `sectionType` fields
|
|
41
|
+
- Graceful fallback if GA4 is not available (doesn't break functionality)
|
|
42
|
+
|
|
43
|
+
## [2.1.9] - 2025-12-26
|
|
44
|
+
|
|
45
|
+
### ✨ Announcement Resolvers System
|
|
46
|
+
|
|
47
|
+
- **New Announcement Resolvers Utility** – Added centralized color resolution system for Announcement components
|
|
48
|
+
- `resolveAnnouncementColors()` – Intelligent color resolution with guaranteed fallback values
|
|
49
|
+
- `resolveCountdownColors()` – Dedicated color resolver for countdown timer styling
|
|
50
|
+
- Supports both new format (`colors.background`) and legacy format (`bgColor`) for backward compatibility
|
|
51
|
+
|
|
52
|
+
### 🔧 Component Refactoring
|
|
53
|
+
|
|
54
|
+
- **Centralized Color Resolution** – Refactored all Announcement components to use the new resolver system
|
|
55
|
+
- Improved code maintainability and consistency across all announcement types
|
|
56
|
+
|
|
57
|
+
### 🎨 Countdown Timer Styling Improvements
|
|
58
|
+
|
|
59
|
+
- **Enhanced Layout** – Improved visual spacing and sizing
|
|
60
|
+
- Added `gap-1` between countdown digits
|
|
61
|
+
- Reduced digit font size from `text-xl` to `text-lg`
|
|
62
|
+
- Changed line height from `leading-none` to `leading-tight`
|
|
63
|
+
|
|
64
|
+
### 🔧 Type System Updates
|
|
65
|
+
|
|
66
|
+
- **Enhanced Type Definitions** – Updated `IAnnouncement` and `IHero` interfaces
|
|
67
|
+
- Added `active_duration_seconds`, `last_activated_at`, and `last_deactivated_at` fields
|
|
68
|
+
|
|
69
|
+
### 📚 Documentation
|
|
70
|
+
|
|
71
|
+
- **New Announcement Resolvers Documentation** – Added comprehensive guide for the new resolver system
|
|
72
|
+
|
|
73
|
+
## [2.1.8] - 2025-12-25
|
|
74
|
+
|
|
75
|
+
### ✨ Countdown Timer Styling Enhancements
|
|
76
|
+
|
|
77
|
+
- **Enhanced Countdown Timer Color Tokens** – Added comprehensive color customization for countdown timers
|
|
78
|
+
- `counterDigitColor` – Customize the color of countdown digits (default: `#FFFFFF`)
|
|
79
|
+
- `counterDigitBackgroundColor` – Customize the background color of countdown digits (default: `#000000`)
|
|
80
|
+
- `counterTextColor` – Customize the color of labels and separators (default: uses `counterDigitColor`)
|
|
81
|
+
- **Fixed Token Resolution** – Corrected `counterTextColor` token resolution to use proper fallback handling
|
|
82
|
+
- **Documentation Updates** – Added comprehensive countdown timer styling documentation to README
|
|
83
|
+
|
|
84
|
+
## [2.1.5] - 2025-12-05
|
|
85
|
+
|
|
86
|
+
### 🔧 Type System Updates
|
|
87
|
+
|
|
88
|
+
- **Enhanced Type Definitions** – Updated `IAnnouncement` and `IHero` interfaces to match Supabase schema
|
|
89
|
+
- Added `active_start_date: string | null` field
|
|
90
|
+
- Added `active_end_date: string | null` field
|
|
91
|
+
- Made `created_at` and `updated_at` nullable
|
|
92
|
+
- Made `page` field nullable in `IHero`
|
|
93
|
+
|
|
94
|
+
### 🗑️ Breaking Changes
|
|
95
|
+
|
|
96
|
+
- **Removed Theme & Variant System** – Simplified styling system by removing unused theme/variant abstractions
|
|
97
|
+
- Removed `theme` property from `IFragmentStyling`
|
|
98
|
+
- Removed `variant` property from `IFragmentStyling`
|
|
99
|
+
- Focus now exclusively on design tokens, slots, responsive design, and state-based styling
|
|
100
|
+
- Updated all documentation to reflect the simplified system
|
|
101
|
+
|
|
102
|
+
## [2.1.4] - 2025-11-18
|
|
103
|
+
|
|
104
|
+
### ✨ UI/UX Improvements
|
|
105
|
+
|
|
106
|
+
- **Countdown Timer Enhancements** – Improved countdown timer visual design
|
|
107
|
+
- Removed background color from countdown digits for cleaner appearance
|
|
108
|
+
- Increased digit font size for better visibility
|
|
109
|
+
- Shortened label text: "Minutes" → "Mins", "Seconds" → "Secs"
|
|
110
|
+
|
|
111
|
+
- **Announcement Component Updates** – Enhanced announcement functionality
|
|
112
|
+
- Countdown timers can now display alongside buttons when both are enabled
|
|
113
|
+
- Improved layout flexibility for countdown announcements with CTAs
|
|
114
|
+
|
|
115
|
+
## [2.1.2] - 2025-10-30
|
|
116
|
+
|
|
117
|
+
### 📚 Documentation Updates
|
|
118
|
+
|
|
119
|
+
- **Enhanced README** – Updated documentation to highlight new click tracking features
|
|
120
|
+
- **Feature Highlights** – Added comprehensive documentation for the enhanced click tracking system
|
|
121
|
+
- **Usage Examples** – Improved examples showing the separation of button destinations and tracking
|
|
122
|
+
|
|
123
|
+
## [2.1.1] - 2025-10-30
|
|
124
|
+
|
|
125
|
+
### 🎯 Enhanced Click Tracking System
|
|
126
|
+
|
|
127
|
+
- **Improved Click Tracking Architecture** – Separated button destinations from click tracking for better user experience
|
|
128
|
+
- `buttonHref` now contains the actual destination URL (no redirect)
|
|
129
|
+
- `clickHref` contains the tracking URL for metrics collection
|
|
130
|
+
- Users go directly to intended destinations instead of through redirects
|
|
131
|
+
- **New `fireClickMetric()` Function** – Advanced click tracking without relying on redirects
|
|
132
|
+
- Uses `fetch()` with `no-cors` mode and `keepalive` for reliable tracking
|
|
133
|
+
- Falls back to Image pixel tracking for maximum compatibility
|
|
134
|
+
- Handles server-side rendering gracefully
|
|
135
|
+
- **Removed Automatic New Tab Behavior** – Links no longer force `target="_blank"`
|
|
136
|
+
- Provides more natural user experience
|
|
137
|
+
- Allows developers to control link behavior explicitly
|
|
138
|
+
- Maintains accessibility with proper ARIA labels
|
|
139
|
+
|
|
140
|
+
### 🛠 Technical Improvements
|
|
141
|
+
|
|
142
|
+
- **Enhanced Component Props** – Both Hero and Announcement components now accept separate tracking parameters
|
|
143
|
+
- `buttonHref` for the actual destination
|
|
144
|
+
- `clickHref` for tracking metrics
|
|
145
|
+
- **Better Error Handling** – Click tracking fails gracefully without affecting user experience
|
|
146
|
+
- **Performance Optimized** – Non-blocking click tracking that doesn't delay navigation
|
|
147
|
+
- **Cross-Browser Compatible** – Works across all modern browsers with appropriate fallbacks
|
|
148
|
+
|
|
149
|
+
### 📝 Usage Examples
|
|
150
|
+
|
|
151
|
+
```typescript
|
|
152
|
+
// The SDK automatically handles the separation of concerns
|
|
153
|
+
const heroContent = {
|
|
154
|
+
title: "Shop Now",
|
|
155
|
+
buttonText: "Get Started",
|
|
156
|
+
buttonLink: "https://example.com/products", // Direct destination
|
|
157
|
+
clickUrlBase: "https://tracking.example.com/click", // Tracking base
|
|
158
|
+
// SDK automatically creates:
|
|
159
|
+
// - buttonHref: "https://example.com/products" (direct link)
|
|
160
|
+
// - clickHref: "https://tracking.example.com/click?u=..." (tracking)
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
<Hero content={heroContent} />;
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### ⚡ Performance Benefits
|
|
167
|
+
|
|
168
|
+
- **Faster Navigation** – Users go directly to destinations without redirect delays
|
|
169
|
+
- **Reliable Tracking** – Click metrics are captured even if users navigate away quickly
|
|
170
|
+
- **Better SEO** – Direct links improve search engine crawling and indexing
|
|
171
|
+
- **Enhanced UX** – More predictable link behavior for better user experience
|
|
172
|
+
|
|
173
|
+
### 🔄 Backward Compatibility
|
|
174
|
+
|
|
175
|
+
- **No Breaking Changes** – All existing implementations continue to work
|
|
176
|
+
- **Automatic Upgrade** – New tracking system activates automatically when `clickUrlBase` is present
|
|
177
|
+
- **Legacy Support** – Existing tracking URLs continue to function as before
|
|
178
|
+
|
|
179
|
+
## [2.1.0] - 2025-10-27
|
|
180
|
+
|
|
181
|
+
### 🎨 Enhanced Hero Styling System
|
|
182
|
+
|
|
183
|
+
- **New Hero Resolvers Utility** – Comprehensive utility system for advanced Hero component customization
|
|
184
|
+
- `resolveHeroColors()` – Intelligent color resolution with fallback handling
|
|
185
|
+
- `resolveHeroTypography()` – Typography settings with font family, size, and line height control
|
|
186
|
+
- `resolveContentWidthClass()` – Dynamic content width management
|
|
187
|
+
- `resolvePosition()` – Content positioning (left, center, right alignment)
|
|
188
|
+
- `resolveHeight()` – Flexible height configuration
|
|
189
|
+
- `renderText()` – Unified text rendering with typography and styling support
|
|
190
|
+
|
|
191
|
+
### ✨ Advanced Typography Features
|
|
192
|
+
|
|
193
|
+
- **Font Family Support** – Built-in support for popular font families:
|
|
194
|
+
- Roboto, Open Sans, Lato, Montserrat, Poppins, Inter, Nunito Sans, Source Sans Pro
|
|
195
|
+
- Custom font family support through `FontKey` type system
|
|
196
|
+
- **Responsive Typography** – Granular control over font sizes and line heights
|
|
197
|
+
- Separate title and description typography settings
|
|
198
|
+
- Tailwind CSS class integration for responsive design
|
|
199
|
+
- **Typography Tokens** – New styling tokens for enhanced typography control:
|
|
200
|
+
- `titleFontSize`, `titleLineHeight`, `titleFont`
|
|
201
|
+
- `descriptionFontSize`, `descriptionLineHeight`, `descriptionFont`
|
|
202
|
+
|
|
203
|
+
### 🏗️ Layout & Positioning Enhancements
|
|
204
|
+
|
|
205
|
+
- **Content Positioning** – New positioning system for Hero content alignment
|
|
206
|
+
- Left, center, and right alignment options
|
|
207
|
+
- Responsive positioning with proper text alignment
|
|
208
|
+
- **Content Width Control** – Dynamic content width management
|
|
209
|
+
- Configurable content container widths
|
|
210
|
+
- Responsive design integration
|
|
211
|
+
- **Height Management** – Flexible height configuration system
|
|
212
|
+
- Custom height classes support
|
|
213
|
+
- Default height fallbacks
|
|
214
|
+
|
|
215
|
+
### 🎯 Developer Experience Improvements
|
|
216
|
+
|
|
217
|
+
- **Type Safety** – Enhanced TypeScript interfaces for all new features
|
|
218
|
+
- `HeroResolvedColors` interface for color resolution
|
|
219
|
+
- `HeroTypographySettings` interface for typography configuration
|
|
220
|
+
- `FontKey` type for font family validation
|
|
221
|
+
- **Utility Functions** – New helper functions for common operations
|
|
222
|
+
- `joinClassNames()` – Safe CSS class concatenation
|
|
223
|
+
- `fallbackColor()` – Color value validation with fallbacks
|
|
224
|
+
- **Better Defaults** – Comprehensive default values for all styling options
|
|
225
|
+
- `DEFAULT_COLORS` for color fallbacks
|
|
226
|
+
- `DEFAULT_TYPOGRAPHY` for typography defaults
|
|
227
|
+
- `FONT_FAMILY_MAP` for font family mappings
|
|
228
|
+
|
|
229
|
+
### 🔄 Backward Compatibility
|
|
230
|
+
|
|
231
|
+
- **Seamless Migration** – All existing Hero components continue to work without changes
|
|
232
|
+
- **Progressive Enhancement** – New features are opt-in and don't affect existing implementations
|
|
233
|
+
- **Legacy Support** – Existing styling approaches remain fully supported
|
|
234
|
+
|
|
235
|
+
### 📝 Usage Examples
|
|
236
|
+
|
|
237
|
+
```typescript
|
|
238
|
+
// Enhanced Hero with new typography and positioning
|
|
239
|
+
const heroContent = {
|
|
240
|
+
title: "Welcome to Our Store",
|
|
241
|
+
description: "Discover amazing products",
|
|
242
|
+
buttonText: "Shop Now",
|
|
243
|
+
buttonLink: "/products",
|
|
244
|
+
imageUrl: "https://example.com/hero.jpg",
|
|
245
|
+
|
|
246
|
+
styling: {
|
|
247
|
+
tokens: {
|
|
248
|
+
colors: {
|
|
249
|
+
title: "#ffffff",
|
|
250
|
+
text: "#f0f0f0",
|
|
251
|
+
button: "#007bff",
|
|
252
|
+
buttonText: "#ffffff",
|
|
253
|
+
background: "#1a1a1a",
|
|
254
|
+
},
|
|
255
|
+
typography: {
|
|
256
|
+
titleFont: "montserrat",
|
|
257
|
+
titleFontSize: "text-6xl",
|
|
258
|
+
titleLineHeight: "leading-tight",
|
|
259
|
+
descriptionFont: "inter",
|
|
260
|
+
descriptionFontSize: "text-xl",
|
|
261
|
+
descriptionLineHeight: "leading-relaxed",
|
|
262
|
+
},
|
|
263
|
+
layout: {
|
|
264
|
+
contentWidth: "max-w-4xl",
|
|
265
|
+
position: "center",
|
|
266
|
+
height: "min-h-screen",
|
|
267
|
+
},
|
|
268
|
+
},
|
|
269
|
+
},
|
|
270
|
+
};
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
### 🛠 Technical Improvements
|
|
274
|
+
|
|
275
|
+
- **Performance Optimized** – Efficient color and typography resolution
|
|
276
|
+
- **Memory Efficient** – Optimized utility functions with minimal overhead
|
|
277
|
+
- **Tree Shakeable** – Individual utility functions can be imported separately
|
|
278
|
+
- **CSS-in-JS Ready** – Full compatibility with styled-components and emotion
|
|
279
|
+
|
|
280
|
+
## [1.0.6] - 2025-10-16
|
|
281
|
+
|
|
282
|
+
### 🚀 Next.js Caching Fix
|
|
283
|
+
|
|
284
|
+
- **Fixed Vercel/Next.js Caching Issues** – Resolved aggressive caching that prevented fresh data from appearing in production deployments
|
|
285
|
+
- Added `cache: 'no-store'` by default for all `fetchResource()` calls
|
|
286
|
+
- Added Next.js-specific `revalidate: 0` configuration
|
|
287
|
+
- Added cache-busting headers (`Cache-Control`, `Pragma`) to prevent CDN caching
|
|
288
|
+
- Smart environment detection for Next.js vs other frameworks
|
|
289
|
+
|
|
290
|
+
### ✨ New Cache Management Features
|
|
291
|
+
|
|
292
|
+
- **Cache Configuration Options** – Added optional `cacheOptions` parameter to `fetchResource()`
|
|
293
|
+
- `cache`: Control request cache mode (default: 'no-store' for fresh data)
|
|
294
|
+
- `revalidate`: Next.js revalidation time in seconds (default: 0)
|
|
295
|
+
- `tags`: Next.js cache tags for selective invalidation
|
|
296
|
+
- **Cache Invalidation Utilities** – New helper functions for cache management
|
|
297
|
+
- `revalidateFragmentCache()` – Invalidate all or specific Fragment caches
|
|
298
|
+
- `revalidateResourceType()` – Invalidate cache for specific resource type
|
|
299
|
+
- `revalidateAllFragmentCaches()` – Clear all Fragment-related caches
|
|
300
|
+
- `createCacheTag()` / `createCacheTags()` – Generate cache tags
|
|
301
|
+
|
|
302
|
+
### 🛠 Technical Improvements
|
|
303
|
+
|
|
304
|
+
- **Environment Detection** – Automatic Next.js environment detection for optimal cache settings
|
|
305
|
+
- **Backward Compatibility** – All existing code continues to work without changes
|
|
306
|
+
- **TypeScript Support** – Full type definitions for new cache options
|
|
307
|
+
|
|
308
|
+
### 📝 Usage Examples
|
|
309
|
+
|
|
310
|
+
```typescript
|
|
311
|
+
// Default behavior - always fresh data (recommended)
|
|
312
|
+
const announcements = await fetchResource({
|
|
313
|
+
baseUrl: process.env.FRAGMENT_BASE_URL,
|
|
314
|
+
apiKey: process.env.FRAGMENT_API_KEY,
|
|
315
|
+
type: ResourceType.Announcements,
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
// Optional: Enable caching for performance
|
|
319
|
+
const cachedHeroes = await fetchResource({
|
|
320
|
+
baseUrl: process.env.FRAGMENT_BASE_URL,
|
|
321
|
+
apiKey: process.env.FRAGMENT_API_KEY,
|
|
322
|
+
type: ResourceType.HeroBanners,
|
|
323
|
+
cacheOptions: {
|
|
324
|
+
cache: "default",
|
|
325
|
+
revalidate: 300, // 5 minutes
|
|
326
|
+
},
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
// Cache invalidation (server-side only)
|
|
330
|
+
import { revalidateResourceType } from "fragment-headless-sdk";
|
|
331
|
+
await revalidateResourceType(ResourceType.Announcements);
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
### ⚠️ Migration Notes
|
|
335
|
+
|
|
336
|
+
- **No breaking changes** – Existing code works without modification
|
|
337
|
+
- **Fresh data by default** – Your database updates will now appear immediately in production
|
|
338
|
+
- **Opt-in caching** – Use `cacheOptions` if you want to enable caching for performance
|
|
339
|
+
|
|
340
|
+
## [1.0.5] - 2025-09-28
|
|
341
|
+
|
|
342
|
+
### ✨ New Features
|
|
343
|
+
|
|
344
|
+
- **Metrics Tracking** – Added built-in view and click tracking for both **Hero** and **Announcement** sections.
|
|
345
|
+
- Each resource’s `content` object now includes two server-generated fields:
|
|
346
|
+
- `impressionUrl` – 1×1 pixel URL automatically fired when the component enters the viewport.
|
|
347
|
+
- `clickUrlBase` – base redirect URL used to record button clicks before sending the user to the final destination.
|
|
348
|
+
- The SDK’s `<Hero>` and `<Announcement>` components now automatically:
|
|
349
|
+
- trigger a view pixel when visible, and
|
|
350
|
+
- wrap their CTA buttons with a signed click-tracking redirect.
|
|
351
|
+
|
|
352
|
+
### 🛠 Technical Notes
|
|
353
|
+
|
|
354
|
+
- The `makeSignedMetricUrls` helper was refactored to attach `impressionUrl` and `clickUrlBase` **inside the `content` object** for each item returned by the API.
|
|
355
|
+
- New client-side utilities exported from `utils`:
|
|
356
|
+
- `buildClickUrl()` – safely appends the final destination (`&u=...`) to a signed `clickUrlBase`.
|
|
357
|
+
- `fireImpressionWhenVisible()` – fires a pixel only once when an element is at least 30 % visible.
|
|
358
|
+
|
|
359
|
+
### ⚠️ Migration Notes
|
|
360
|
+
|
|
361
|
+
- **No breaking changes.**
|
|
362
|
+
Existing components continue to work; the new tracking is automatic when you upgrade to v1.0.5.
|
|
363
|
+
- If you build custom CTAs outside the provided components, use the new helpers to track clicks and views manually.
|
|
364
|
+
|
|
365
|
+
## [1.0.4] - 2025-09-27
|
|
366
|
+
|
|
367
|
+
- **Types:** `IHero` now includes `views_count: number` and `clicks_count: number`.
|
|
368
|
+
|
|
369
|
+
### 📝 Notes
|
|
370
|
+
|
|
371
|
+
- No breaking changes..
|
|
372
|
+
|
|
373
|
+
## [1.0.3] - 2025-09-21
|
|
374
|
+
|
|
375
|
+
### 🎨 UI/UX Improvements
|
|
376
|
+
|
|
377
|
+
- **Announcement Type Rename** - Changed `AnnouncementType.Announcement` to `AnnouncementType.Static` for better clarity
|
|
378
|
+
- **Countdown Timer Styling** - Removed white background from countdown timer for cleaner appearance
|
|
379
|
+
- **Layout Optimization** - Improved announcement banner layout with:
|
|
380
|
+
- Removed top/bottom padding (`py-3`) for more compact design
|
|
381
|
+
- Added 50px minimum height for consistent banner sizing
|
|
382
|
+
- Enhanced vertical centering of all content elements
|
|
383
|
+
- **Timer Digit Sizing** - Made countdown timer digits smaller and more compact:
|
|
384
|
+
- Reduced digit size from 24×28px to 20×24px
|
|
385
|
+
- Changed font size from `text-xl` to `text-base`
|
|
386
|
+
|
|
387
|
+
### 🔧 Technical Changes
|
|
388
|
+
|
|
389
|
+
- Updated `announcementTypes` array to reflect new "Static" label
|
|
390
|
+
- Improved flexbox layout for better vertical alignment
|
|
391
|
+
- Maintained responsive design across all screen sizes
|
|
392
|
+
|
|
393
|
+
## [1.0.2] - 2025-09-20
|
|
394
|
+
|
|
395
|
+
### 🔄 Breaking Changes
|
|
396
|
+
|
|
397
|
+
- **Component Naming** - Renamed `Banner` component and all related types to `Announcement`
|
|
398
|
+
- `Banner` → `Announcement`
|
|
399
|
+
- `IBannerContent` → `IAnnouncementContent`
|
|
400
|
+
- `IBanner` → `IAnnouncement`
|
|
401
|
+
- `BannerType` → `AnnouncementType`
|
|
402
|
+
- `BannerStatus` → `AnnouncementStatus`
|
|
403
|
+
- `BannerButton` → `AnnouncementButton`
|
|
404
|
+
- `BannerStyles` → `AnnouncementStyles`
|
|
405
|
+
- `bannerHtml` property → `announcementHtml`
|
|
406
|
+
|
|
407
|
+
- **Resource Type Updates** - Updated resource type enums for consistency
|
|
408
|
+
- `ResourceType.HeroSections` → `ResourceType.HeroBanners`
|
|
409
|
+
- `ResourceType.Banners` → `ResourceType.Announcements`
|
|
410
|
+
|
|
411
|
+
### 🔗 API Endpoint Changes
|
|
412
|
+
|
|
413
|
+
- Updated API endpoints to match new naming:
|
|
414
|
+
- `/api/v1/hero-sections` → `/api/v1/hero-banners`
|
|
415
|
+
- `/api/v1/banners` → `/api/v1/announcements`
|
|
416
|
+
|
|
417
|
+
### 📚 Documentation
|
|
418
|
+
|
|
419
|
+
- Updated all documentation to reflect new component and type names
|
|
420
|
+
- Updated README.md examples with new ResourceType values
|
|
421
|
+
- Updated code examples throughout
|
|
422
|
+
|
|
423
|
+
### 🛠️ Migration Guide
|
|
424
|
+
|
|
425
|
+
To update your existing code:
|
|
426
|
+
|
|
427
|
+
```typescript
|
|
428
|
+
// Before (v1.0.1)
|
|
429
|
+
import {
|
|
430
|
+
Banner,
|
|
431
|
+
BannerType,
|
|
432
|
+
IBannerContent,
|
|
433
|
+
ResourceType,
|
|
434
|
+
} from "fragment-headless-sdk";
|
|
435
|
+
|
|
436
|
+
const banners = await fetchResource({
|
|
437
|
+
type: ResourceType.Banners,
|
|
438
|
+
});
|
|
439
|
+
|
|
440
|
+
<Banner content={bannerContent} type={BannerType.Standard} />;
|
|
441
|
+
|
|
442
|
+
// After (v1.0.2)
|
|
443
|
+
import {
|
|
444
|
+
Announcement,
|
|
445
|
+
AnnouncementType,
|
|
446
|
+
IAnnouncementContent,
|
|
447
|
+
ResourceType,
|
|
448
|
+
} from "fragment-headless-sdk";
|
|
449
|
+
|
|
450
|
+
const announcements = await fetchResource({
|
|
451
|
+
type: ResourceType.Announcements,
|
|
452
|
+
});
|
|
453
|
+
|
|
454
|
+
<Announcement
|
|
455
|
+
content={announcementContent}
|
|
456
|
+
type={AnnouncementType.Announcement}
|
|
457
|
+
/>;
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
## [1.0.1] - 2025-09-07
|
|
461
|
+
|
|
462
|
+
### 🎉 Initial Release
|
|
463
|
+
|
|
464
|
+
The official SDK for integrating with fragment-shopify CMS. Production-ready with full API key authentication support.
|
|
465
|
+
|
|
466
|
+
### Features
|
|
467
|
+
|
|
468
|
+
- **Complete TypeScript Support** - Full type definitions for all components and API responses
|
|
469
|
+
- **React Components** - Pre-built Hero and Announcement components with responsive design
|
|
470
|
+
- **API Integration** - Built-in utilities for fetching sections from fragment-shopify app
|
|
471
|
+
- **Production Ready** - Full API key authentication with v1 endpoints
|
|
472
|
+
- **Tailwind CSS** - Styled components with customizable design system
|
|
473
|
+
|
|
474
|
+
### Components
|
|
475
|
+
|
|
476
|
+
- **Hero Component** - Responsive hero sections with desktop/mobile layouts
|
|
477
|
+
- Support for images, videos, and call-to-action buttons
|
|
478
|
+
- Customizable content and styling
|
|
479
|
+
- **Announcement Component** - Flexible announcement bars with multiple display types
|
|
480
|
+
- Standard, marquee, and countdown announcement types
|
|
481
|
+
- `AnnouncementButton` and `CountdownTimer` sub-components
|
|
482
|
+
|
|
483
|
+
### API Integration
|
|
484
|
+
|
|
485
|
+
- **`fetchResource()` Function** - Simple API for fetching sections
|
|
486
|
+
- **API Key Authentication** - Secure authentication using `keyId:secret` format
|
|
487
|
+
- **v1 Endpoints** - Production endpoints (`/api/v1/announcements`, `/api/v1/hero-banners`)
|
|
488
|
+
- **Error Handling** - Comprehensive error handling and logging
|
|
489
|
+
- **Type Safety** - Full TypeScript support for all API responses
|
|
490
|
+
|
|
491
|
+
### Usage
|
|
492
|
+
|
|
493
|
+
```tsx
|
|
494
|
+
import {
|
|
495
|
+
fetchResource,
|
|
496
|
+
ResourceType,
|
|
497
|
+
Hero,
|
|
498
|
+
Announcement,
|
|
499
|
+
} from "fragment-headless-sdk";
|
|
500
|
+
|
|
501
|
+
// Fetch data
|
|
502
|
+
const heroes = await fetchResource({
|
|
503
|
+
baseUrl: process.env.EXTERNAL_API_URL,
|
|
504
|
+
apiKey: process.env.FRAGMENT_API_KEY,
|
|
505
|
+
type: ResourceType.HeroBanners,
|
|
506
|
+
});
|
|
507
|
+
|
|
508
|
+
// Render components
|
|
509
|
+
<Hero content={heroes[0]?.content} />;
|
|
510
|
+
```
|
|
511
|
+
|
|
512
|
+
### Environment Variables
|
|
513
|
+
|
|
514
|
+
```bash
|
|
515
|
+
EXTERNAL_API_URL=https://your-fragment-app.vercel.app
|
|
516
|
+
FRAGMENT_API_KEY=bh_a1b2c3d4e5f6:your-64-char-secret
|
|
517
|
+
```
|
|
518
|
+
|
|
519
|
+
---
|
|
520
|
+
|
|
521
|
+
## Upcoming Changes
|
|
522
|
+
|
|
523
|
+
### v1.1.0 (Planned)
|
|
524
|
+
|
|
525
|
+
- Enhanced error handling with specific error types
|
|
526
|
+
- Support for additional section types
|
|
527
|
+
- Caching and performance optimizations
|
|
528
|
+
|
|
529
|
+
### v1.2.0 (Planned)
|
|
530
|
+
|
|
531
|
+
- Real-time updates via webhooks
|
|
532
|
+
- Advanced filtering and sorting options
|
|
533
|
+
- Batch operations support
|
|
534
|
+
- TypeScript strict mode compatibility
|
|
535
|
+
|
|
536
|
+
---
|
|
537
|
+
|
|
538
|
+
## Support
|
|
539
|
+
|
|
540
|
+
- **Documentation**: [README.md](./README.md)
|
|
541
|
+
- **NPM Package**: https://www.npmjs.com/package/fragment-shopify-sdk
|
|
542
|
+
- **Issues**: Please report issues in the GitHub repository
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fragment-headless-sdk",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.3.1",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -12,7 +12,8 @@
|
|
|
12
12
|
"./styles": "./dist/styles/fragment-sdk.css"
|
|
13
13
|
},
|
|
14
14
|
"files": [
|
|
15
|
-
"dist"
|
|
15
|
+
"dist",
|
|
16
|
+
"docs/CHANGELOG.md"
|
|
16
17
|
],
|
|
17
18
|
"scripts": {
|
|
18
19
|
"build": "tsc"
|
package/readme.md
CHANGED
|
@@ -4,7 +4,7 @@ The official SDK for integrating with Fragment-Shopify CMS. Provides React compo
|
|
|
4
4
|
|
|
5
5
|
## ✨ What's New
|
|
6
6
|
|
|
7
|
-
**v2.
|
|
7
|
+
**v2.3.1** - Enhanced attribution tracking with session storage and global API
|
|
8
8
|
|
|
9
9
|
> See [CHANGELOG.md](./docs/CHANGELOG.md) for full release history
|
|
10
10
|
|
|
@@ -80,6 +80,15 @@ Fragment-Shopify App (CMS) → API Endpoint → fragment-headless-sdk (Consumer)
|
|
|
80
80
|
- 🛡️ **Graceful Fallback**: Works even if GA4 is not configured (doesn't break functionality)
|
|
81
81
|
- 📦 **Exported Types**: `SectionType` enum available for consumer use
|
|
82
82
|
|
|
83
|
+
### Attribution Tracking System (v2.4.0+)
|
|
84
|
+
|
|
85
|
+
- 🎯 **Session-Based Attribution**: Tracks which section was clicked for purchase/add-to-cart attribution
|
|
86
|
+
- 💾 **SessionStorage Integration**: Uses sessionStorage for session-scoped attribution (last-click wins)
|
|
87
|
+
- 🌐 **Global API Access**: Exposed as `window.fragmentAttribution` for Shopify theme integrations
|
|
88
|
+
- 🔒 **Type-Safe**: Full TypeScript support with proper type definitions
|
|
89
|
+
- ✅ **Data Validation**: Automatic validation and cleanup of corrupted attribution data
|
|
90
|
+
- 🧹 **Auto-Cleanup**: Invalid or corrupted data is automatically cleared from storage
|
|
91
|
+
|
|
83
92
|
---
|
|
84
93
|
|
|
85
94
|
## 📦 Installation
|
|
@@ -117,7 +126,7 @@ module.exports = {
|
|
|
117
126
|
"./components/**/*.{js,ts,jsx,tsx,mdx}",
|
|
118
127
|
path.join(
|
|
119
128
|
__dirname,
|
|
120
|
-
"node_modules/fragment-headless-sdk/dist/**/*.{js,ts,jsx,tsx}"
|
|
129
|
+
"node_modules/fragment-headless-sdk/dist/**/*.{js,ts,jsx,tsx}",
|
|
121
130
|
),
|
|
122
131
|
],
|
|
123
132
|
theme: {
|
|
@@ -481,6 +490,105 @@ const announcementContent = {
|
|
|
481
490
|
};
|
|
482
491
|
```
|
|
483
492
|
|
|
493
|
+
## 🎯 Attribution Tracking
|
|
494
|
+
|
|
495
|
+
The SDK automatically tracks which section was clicked for attribution purposes. This is useful for tracking conversions (purchases, add-to-cart) back to the original section interaction.
|
|
496
|
+
|
|
497
|
+
### Automatic Attribution
|
|
498
|
+
|
|
499
|
+
When a user clicks a button in a Hero or Announcement section, the SDK automatically stores attribution data in `sessionStorage`:
|
|
500
|
+
|
|
501
|
+
```typescript
|
|
502
|
+
// Attribution is automatically set when clicks occur
|
|
503
|
+
// No additional code needed - it happens automatically!
|
|
504
|
+
```
|
|
505
|
+
|
|
506
|
+
### Global API (Shopify Theme Integration)
|
|
507
|
+
|
|
508
|
+
For Shopify theme integrations, attribution data is exposed globally via `window.fragmentAttribution`:
|
|
509
|
+
|
|
510
|
+
```javascript
|
|
511
|
+
// Get current attribution data
|
|
512
|
+
const attribution = window.fragmentAttribution.get();
|
|
513
|
+
// Returns: { sectionId: "uuid", sectionType: "announcement" | "hero_banner", timestamp: 1234567890 }
|
|
514
|
+
// Or: null if no attribution exists
|
|
515
|
+
|
|
516
|
+
// Manually set attribution (usually not needed - SDK handles this automatically)
|
|
517
|
+
window.fragmentAttribution.set("section-uuid", "announcement");
|
|
518
|
+
|
|
519
|
+
// Clear attribution (e.g., after successful conversion tracking)
|
|
520
|
+
window.fragmentAttribution.clear();
|
|
521
|
+
```
|
|
522
|
+
|
|
523
|
+
### Programmatic Access
|
|
524
|
+
|
|
525
|
+
You can also import and use attribution functions directly:
|
|
526
|
+
|
|
527
|
+
```typescript
|
|
528
|
+
import { getAttribution, clearAttribution } from "fragment-headless-sdk";
|
|
529
|
+
|
|
530
|
+
// Get attribution data
|
|
531
|
+
const attribution = getAttribution();
|
|
532
|
+
if (attribution) {
|
|
533
|
+
console.log(`User clicked section: ${attribution.sectionId}`);
|
|
534
|
+
console.log(`Section type: ${attribution.sectionType}`);
|
|
535
|
+
console.log(`Clicked at: ${new Date(attribution.timestamp)}`);
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
// Clear attribution after tracking conversion
|
|
539
|
+
clearAttribution();
|
|
540
|
+
```
|
|
541
|
+
|
|
542
|
+
### Use Cases
|
|
543
|
+
|
|
544
|
+
**Purchase Attribution:**
|
|
545
|
+
|
|
546
|
+
```javascript
|
|
547
|
+
// In your Shopify checkout success page
|
|
548
|
+
if (window.fragmentAttribution) {
|
|
549
|
+
const attribution = window.fragmentAttribution.get();
|
|
550
|
+
if (attribution) {
|
|
551
|
+
// Track purchase back to the section click
|
|
552
|
+
// e.g., send to analytics, update database, etc.
|
|
553
|
+
trackPurchase(attribution.sectionId, attribution.sectionType);
|
|
554
|
+
|
|
555
|
+
// Clear attribution after tracking
|
|
556
|
+
window.fragmentAttribution.clear();
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
```
|
|
560
|
+
|
|
561
|
+
**Add-to-Cart Attribution:**
|
|
562
|
+
|
|
563
|
+
```javascript
|
|
564
|
+
// In your add-to-cart handler
|
|
565
|
+
if (window.fragmentAttribution) {
|
|
566
|
+
const attribution = window.fragmentAttribution.get();
|
|
567
|
+
if (attribution) {
|
|
568
|
+
// Track add-to-cart back to the section click
|
|
569
|
+
trackAddToCart(attribution.sectionId, attribution.sectionType);
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
```
|
|
573
|
+
|
|
574
|
+
### Attribution Data Structure
|
|
575
|
+
|
|
576
|
+
```typescript
|
|
577
|
+
interface FragmentAttribution {
|
|
578
|
+
sectionId: string; // UUID of the section that was clicked
|
|
579
|
+
sectionType: SectionType; // "announcement" | "hero_banner"
|
|
580
|
+
timestamp: number; // Unix timestamp of when the click occurred
|
|
581
|
+
}
|
|
582
|
+
```
|
|
583
|
+
|
|
584
|
+
### Features
|
|
585
|
+
|
|
586
|
+
- ✅ **Session-Scoped**: Attribution data persists for the browser session only
|
|
587
|
+
- ✅ **Last-Click Wins**: New clicks overwrite previous attribution data
|
|
588
|
+
- ✅ **Type-Safe**: Full TypeScript support with proper type definitions
|
|
589
|
+
- ✅ **Data Validation**: Automatically validates and cleans corrupted data
|
|
590
|
+
- ✅ **Graceful Degradation**: Works even if sessionStorage is unavailable
|
|
591
|
+
|
|
484
592
|
## 🔑 API Key Setup
|
|
485
593
|
|
|
486
594
|
Before using the SDK, you need to generate an API key from your Fragment-Shopify app:
|