diff --git a/src/macros_new/gowlers-tracking-ledger/scripts/gowlers-tracking-ledger.js b/src/macros_new/gowlers-tracking-ledger/scripts/gowlers-tracking-ledger.js index d3d35072..79a3e8a7 100644 --- a/src/macros_new/gowlers-tracking-ledger/scripts/gowlers-tracking-ledger.js +++ b/src/macros_new/gowlers-tracking-ledger/scripts/gowlers-tracking-ledger.js @@ -12,6 +12,7 @@ const DEFAULT_TRACKING = Object.freeze({ hp: true, xp: true, currency: true }); const DEFAULT_CHAT = Object.freeze({ hp: false, xp: false, currency: false }); const SETTINGS_VERSION = 2; const COIN_ORDER = ["pp", "gp", "sp", "cp"]; +const ENCOUNTER_FLAG = "pf1EncounterHistory"; const STAT_CONFIGS = { hp: { @@ -103,6 +104,11 @@ async function initializeModule() { }) ); + // Track combat encounters + Hooks.on("combatStart", (combat) => onCombatStart(combat)); + Hooks.on("combatEnd", (combat) => onCombatEnd(combat)); + Hooks.on("updateCombat", (combat) => onCombatUpdate(combat)); + const api = { initialized: true, openConfig: () => new TrackingLedgerConfig().render(true), @@ -284,7 +290,9 @@ function findHeaderByText(root, text) { function openHistoryDialog(actor, initialTab = "hp") { if (!actor) return; const content = buildHistoryContent(actor, initialTab); - new Dialog( + + // Create a custom dialog with header button + const dialog = new Dialog( { title: `${actor.name}: Log`, content, @@ -293,8 +301,107 @@ function openHistoryDialog(actor, initialTab = "hp") { { width: 720, classes: ["pf1-history-dialog"], + render: (html) => { + // html is jQuery object of the dialog element + // Find the content root within the dialog + const root = html.find('[data-history-root]')[0]; + if (!root) { + console.warn("[History Dialog] Root element not found"); + return; + } + + console.log("[History Dialog] Root element found, setting up tabs"); + + // Get all tab buttons and panels + const buttons = Array.from(root.querySelectorAll('.history-tab-link')); + const panels = Array.from(root.querySelectorAll('.history-panel')); + + console.log(`[History Dialog] Found ${buttons.length} tabs and ${panels.length} panels`); + + // Tab activation function + const activateTab = (tabId) => { + console.log(`[History Dialog] Activating tab: ${tabId}`); + buttons.forEach((btn) => { + const shouldBeActive = btn.dataset.historyTab === tabId; + btn.classList.toggle('active', shouldBeActive); + }); + panels.forEach((panel) => { + const shouldBeActive = panel.dataset.historyPanel === tabId; + panel.style.display = shouldBeActive ? 'block' : 'none'; + panel.classList.toggle('active', shouldBeActive); + }); + }; + + // Bind tab click handlers using event delegation + buttons.forEach((btn, index) => { + btn.style.cursor = 'pointer'; + btn.addEventListener('click', function(event) { + event.preventDefault(); + event.stopPropagation(); + const tabId = this.dataset.historyTab; + console.log(`[History Dialog] Tab clicked: ${tabId}`); + activateTab(tabId); + return false; + }); + }); + + // Bind config button in content + const configBtn = root.querySelector('[data-action="open-config"]'); + if (configBtn) { + configBtn.addEventListener('click', (event) => { + event.preventDefault(); + event.stopPropagation(); + console.log("[History Dialog] Config button clicked (content)"); + const api = window.GowlersTrackingLedger; + if (api?.openConfigForActor) { + api.openConfigForActor(actor.id); + } else if (api?.openConfig) { + api.openConfig(); + } + }); + } + + // Add config button to dialog header if user is GM + if (game.user?.isGM) { + const header = html.find('.dialog-header')[0]; + if (header) { + const existingBtn = header.querySelector('[data-history-config-header]'); + if (!existingBtn) { + const configHeaderBtn = document.createElement('button'); + configHeaderBtn.type = 'button'; + configHeaderBtn.className = 'history-config-header-btn'; + configHeaderBtn.setAttribute('data-history-config-header', 'true'); + configHeaderBtn.setAttribute('title', 'Configure Actor Tracking'); + configHeaderBtn.innerHTML = ''; + configHeaderBtn.style.cssText = 'border: none; background: transparent; color: #666; cursor: pointer; padding: 8px 10px; margin-right: 8px; transition: color 0.2s;'; + configHeaderBtn.addEventListener('mouseover', () => configHeaderBtn.style.color = '#333'); + configHeaderBtn.addEventListener('mouseout', () => configHeaderBtn.style.color = '#666'); + configHeaderBtn.addEventListener('click', (event) => { + event.preventDefault(); + event.stopPropagation(); + console.log("[History Dialog] Config button clicked (header)"); + const api = window.GowlersTrackingLedger; + if (api?.openConfigForActor) { + api.openConfigForActor(actor.id); + } else if (api?.openConfig) { + api.openConfig(); + } + }); + const closeBtn = header.querySelector('.close'); + if (closeBtn) { + header.insertBefore(configHeaderBtn, closeBtn); + } else { + header.appendChild(configHeaderBtn); + } + console.log("[History Dialog] Config header button added"); + } + } + } + }, } - ).render(true); + ); + + dialog.render(true); } function buildHistoryContent(actor, initialTab = "hp") { @@ -309,7 +416,7 @@ function buildHistoryContent(actor, initialTab = "hp") { { label: "HP", render: (entry) => entry.value }, { label: "Δ", render: (entry) => entry.diff }, { label: "User", render: (entry) => entry.user ?? "" }, - { label: "Source", render: (entry) => entry.source ?? "" }, + { label: "Encounter", render: (entry) => entry.encounterId ? entry.encounterId.slice(0, 8) : "N/A" }, ], }, { @@ -321,7 +428,7 @@ function buildHistoryContent(actor, initialTab = "hp") { { label: "XP", render: (entry) => entry.value }, { label: "Δ", render: (entry) => entry.diff }, { label: "User", render: (entry) => entry.user ?? "" }, - { label: "Source", render: (entry) => entry.source ?? "" }, + { label: "Encounter", render: (entry) => entry.encounterId ? entry.encounterId.slice(0, 8) : "N/A" }, ], }, { @@ -333,7 +440,19 @@ function buildHistoryContent(actor, initialTab = "hp") { { label: "Totals", render: (entry) => entry.value }, { label: "Δ", render: (entry) => entry.diff }, { label: "User", render: (entry) => entry.user ?? "" }, - { label: "Source", render: (entry) => entry.source ?? "" }, + { label: "Encounter", render: (entry) => entry.encounterId ? entry.encounterId.slice(0, 8) : "N/A" }, + ], + }, + { + id: "encounters", + label: "Encounters", + flag: ENCOUNTER_FLAG, + columns: [ + { label: "Encounter ID", render: (entry) => entry.encounterID.slice(0, 8) }, + { label: "Date", render: (entry) => formatDate(entry.dateCreated) }, + { label: "Status", render: (entry) => entry.status ?? "unknown" }, + { label: "Rounds", render: (entry) => entry.rounds || 0 }, + { label: "Participants", render: (entry) => entry.participants ? entry.participants.length : 0 }, ], }, ]; @@ -382,33 +501,6 @@ function buildHistoryContent(actor, initialTab = "hp") { ${toolbar}