* HKILD Section Engagement Tracking for Wix
*
* Tracks:
* - Time spent in each section (using Intersection Observer)
* - Button clicks in each section
*
* Sends data to Wix Collection: hkild_section_engagement
*
* Installation:
* 1. Add this code to Wix Custom Code element
* 2. Place on PD mobile page
*/
(function() {
'use strict';
// Configuration
const config = {
collectionId: 'section_engagement',
sections: [
{ id: 'hero', name: 'Hero + Stats', selector: '.hero, [class*="hero"]' },
{ id: 'honest-check', name: '報讀前問自己', selector: '[class*="honest"]' },
{ id: 'four-advantages', name: '四大核心優勢', selector: '[class*="advantage"]' },
{ id: 'course-content', name: '理論實戰並重', selector: '[class*="course"], [class*="content"]' },
{ id: 'live-practice', name: '實戰練習', selector: '[class*="practice"], [class*="live"]' },
{ id: 'graduation', name: '畢業成果', selector: '[class*="graduate"], [class*="graduation"]' },
{ id: 'outcomes', name: '課程成果', selector: '[class*="outcome"]' },
{ id: 'testimonials', name: '學員真實分享', selector: '[class*="testimonial"]' },
{ id: 'schedule', name: '開班時間', selector: '[class*="schedule"], [class*="time"]' },
{ id: 'faq', name: '常見問題', selector: '[class*="faq"], [class*="question"]' }
],
flushInterval: 30000 // Send data every 30 seconds
};
// Track state
const state = {
sections: {},
pendingData: [],
flushTimer: null
};
// Initialize section tracking state
config.sections.forEach(section => {
state.sections[section.id] = {
name: section.name,
timeSpent: 0,
entered: 0,
left: 0,
lastEntered: null,
isCurrentlyIn: false
};
});
/**
* Format date as YYYY-MM-DD
*/
function getDateString() {
const now = new Date();
return now.toISOString().split('T')[0];
}
/**
* Get ISO timestamp
*/
function getTimestamp() {
return new Date().toISOString();
}
/**
* Send data to Wix Collection
*/
async function sendDataToWix(data) {
try {
// Use Wix Data API to insert item
const response = await fetch(`https://www.wixapis.com/v1/contacts/me`, {
method: 'GET',
headers: {
'Authorization': wixLocation.authorization
}
});
if (response.ok) {
// If we can authenticate, send data
const insertResponse = await fetch(
`https://www.wixapis.com/wix-data/v2/items/${config.collectionId}`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': wixLocation.authorization
},
body: JSON.stringify({
dataItem: {
data: {
section_id: data.sectionId,
page_url: window.location.href,
time_spent: data.timeSpent,
button_clicks: data.buttonClicks || 0,
last_updated: new Date().toISOString().split('T')[0],
visitor_id: data.visitorId || 'anonymous'
}
}
})
}
);
if (insertResponse.ok) {
console.log('✅ Tracking data sent to Wix:', data);
} else {
console.warn('⚠️ Failed to send tracking data to Wix:', insertResponse.status);
}
}
} catch (error) {
console.warn('⚠️ Could not send to Wix API:', error.message);
// Fallback: store in localStorage as backup
saveToLocalStorage(data);
}
}
/**
* Fallback: save to localStorage
*/
function saveToLocalStorage(data) {
try {
const key = 'hkild_user_behavior';
const existing = JSON.parse(localStorage.getItem(key) || '{}');
const today = getDateString();
if (!existing[today]) {
existing[today] = { sections: {}, buttonClicks: [] };
}
// Update section data
existing[today].sections[data.sectionId] = {
timeSpent: data.timeSpent,
entered: data.entered,
left: data.left
};
localStorage.setItem(key, JSON.stringify(existing));
} catch (e) {
console.warn('localStorage not available:', e);
}
}
/**
* Flush pending data to Wix
*/
async function flushData() {
if (state.pendingData.length === 0) return;
const dataToSend = [...state.pendingData];
state.pendingData = [];
for (const data of dataToSend) {
await sendDataToWix(data);
// Small delay between requests
await new Promise(resolve => setTimeout(resolve, 100));
}
}
/**
* Record section entry
*/
function recordSectionEntry(sectionId) {
if (!state.sections[sectionId]) return;
const section = state.sections[sectionId];
section.entered++;
section.lastEntered = Date.now();
section.isCurrentlyIn = true;
console.log(`📍 Entered: ${section.name}`);
}
/**
* Record section exit
*/
function recordSectionExit(sectionId) {
if (!state.sections[sectionId]) return;
const section = state.sections[sectionId];
if (section.lastEntered) {
const elapsed = Math.round((Date.now() - section.lastEntered) / 1000);
section.timeSpent += elapsed;
section.left++;
console.log(`📍 Exited: ${section.name} (${elapsed}s)`);
}
section.isCurrentlyIn = false;
// Queue data to send
const dataToSend = {
date: getDateString(),
sectionId: sectionId,
sectionName: section.name,
timeSpent: section.timeSpent,
entered: section.entered,
left: section.left,
timestamp: getTimestamp()
};
state.pendingData.push(dataToSend);
}
/**
* Record button click in section
*/
function recordButtonClick(sectionId, buttonText) {
const dataToSend = {
date: getDateString(),
sectionId: sectionId,
sectionName: state.sections[sectionId]?.name || 'Unknown',
buttonText: buttonText,
timestamp: getTimestamp()
};
state.pendingData.push(dataToSend);
console.log(`🔘 Button clicked in ${sectionId}: ${buttonText}`);
}
/**
* Setup Intersection Observer for section tracking
*/
function setupIntersectionObserver() {
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
recordSectionEntry(entry.target.dataset.sectionId);
} else {
recordSectionExit(entry.target.dataset.sectionId);
}
});
}, {
threshold: 0.1 // Trigger when 10% of section is visible
});
// Observe all sections
config.sections.forEach(section => {
const elements = document.querySelectorAll(section.selector);
elements.forEach(el => {
el.dataset.sectionId = section.id;
observer.observe(el);
});
});
console.log('✅ Intersection Observer initialized');
}
/**
* Setup button click tracking
*/
function setupButtonTracking() {
document.addEventListener('click', (event) => {
const button = event.target.closest('button, a[role="button"]');
if (!button) return;
// Find which section this button is in
let section = button.closest('[data-section-id]');
if (section) {
const sectionId = section.dataset.sectionId;
const buttonText = button.textContent.trim().substring(0, 50);
recordButtonClick(sectionId, buttonText);
}
}, true);
console.log('✅ Button tracking initialized');
}
/**
* Initialize everything
*/
function initialize() {
console.log('🚀 HKILD Tracking Initialized');
// Setup observers
setupIntersectionObserver();
setupButtonTracking();
// Auto-flush data periodically
state.flushTimer = setInterval(flushData, config.flushInterval);
// Flush on page unload
window.addEventListener('beforeunload', flushData);
// Expose for debugging
window.hkildTracking = {
getState: () => state,
flushData: flushData,
recordButtonClick: recordButtonClick,
recordSectionEntry: recordSectionEntry,
recordSectionExit: recordSectionExit
};
}
// Start when DOM is ready
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initialize);
} else {
initialize();
}
})();
top of page
ACEPT 是 ACE Personality Typology 的簡稱,中文名稱為「知情行-性格型態學」,由黃堅強博士創立,並於 2015 年開始推廣,它能有效地提升人際關係中的了解與融和。
本工作坊運用ACEPT性格分類,迅速找出自己和他人性格的定位,了解彼此壓力及滿足感
來源,有效地管理情緒,從而創造最佳的相處模式。
-
運用簡單的「ACEPT 性格測試」,快速為自己和他人找到「性格定位」。
-
了解不同性格的滿足感、壓力來源及有效溝通方法。
-
通過實例,將 ACEPT 實際應用到工作關係/ 親密關係/ 家庭關係 3 大範疇。
《ACEPT 個性•優勢發展》3 大特點:
1 性格辨析簡單易明。
2 發掘性格獨特性,明己知人。
3 善用性格優勢解決問題,實用性強。
應用範圍 :
1 工作關係 - 了解上司、同事、下屬不同性格的工作模式,主動營造愉快工作氛圍。
2 親密關係 - 了解不同性格類型愛的語言,避開關係中的摩擦,讓彼此保持親密感覺。
3 家庭關係 - 了解親子、夫婦之間的三角關
係,調合不同教養方案,做到因材施教。
PART-2
實體互動工作坊 (3 小時)
運用互動遊戲及分組討論,令學員認識不同的性格特質,明己知人。
日期:4 月 22 日(星期六)
時間 :2:30pm - 5:30pm
地點:九龍油麻地彌敦行 13 樓 C 室
PART-3
Zoom 工作坊 (2 小時) ,通過學員提供的活學活用實例,彼此交流心得 (前後設Whatsapp 群組交流)。
日期:4 月 29 日
(星期六)
時間 : 2:30pm - 5:30pm
導師: 黃堅強博士
- ACE Personality Typology「知情行 性格型態學」始創人
- 香港生命開展學會•個人成長顧問
課程編號 : ACE 02-2023
費用: HK$720.00 (早鳥優惠$650.00開課前3星期前繳交)
名額: 25人(額滿即止)
報名方法:
請用WhatsApp 69063436 聯絡並提供以下資料報名:
1) 課程編號
2) 中文姓名(出收據用)
3) 聯絡電話號碼 (可接收WhatsApp)
繳交學費方式 :
網上付款 :
轉數快FPS – 直接輸入帳戶電話號碼 69063436
Payme – 請掃瞄QR Code
AlipayHK支付寶HK – 按轉賬 → 輸入69063436 選轉數快 → 輸入金額
銀行付款 :
滙豐銀行櫃位或櫃員機 –現金、轉帳 或 入支票(抬頭 - 香港生命開展學會)
賬戶號碼 :匯豐銀行 - 634-431936-001
(備註: 已繳交學費不會退款,除沒有開班)
**本會收到銀行入數紙或訊息後,會儘快發出公司收據確認**
bottom of page