sth
This commit is contained in:
@@ -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 DEFAULT_CHAT = Object.freeze({ hp: false, xp: false, currency: false });
|
||||||
const SETTINGS_VERSION = 2;
|
const SETTINGS_VERSION = 2;
|
||||||
const COIN_ORDER = ["pp", "gp", "sp", "cp"];
|
const COIN_ORDER = ["pp", "gp", "sp", "cp"];
|
||||||
|
const ENCOUNTER_FLAG = "pf1EncounterHistory";
|
||||||
|
|
||||||
const STAT_CONFIGS = {
|
const STAT_CONFIGS = {
|
||||||
hp: {
|
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 = {
|
const api = {
|
||||||
initialized: true,
|
initialized: true,
|
||||||
openConfig: () => new TrackingLedgerConfig().render(true),
|
openConfig: () => new TrackingLedgerConfig().render(true),
|
||||||
@@ -284,7 +290,9 @@ function findHeaderByText(root, text) {
|
|||||||
function openHistoryDialog(actor, initialTab = "hp") {
|
function openHistoryDialog(actor, initialTab = "hp") {
|
||||||
if (!actor) return;
|
if (!actor) return;
|
||||||
const content = buildHistoryContent(actor, initialTab);
|
const content = buildHistoryContent(actor, initialTab);
|
||||||
new Dialog(
|
|
||||||
|
// Create a custom dialog with header button
|
||||||
|
const dialog = new Dialog(
|
||||||
{
|
{
|
||||||
title: `${actor.name}: Log`,
|
title: `${actor.name}: Log`,
|
||||||
content,
|
content,
|
||||||
@@ -293,8 +301,107 @@ function openHistoryDialog(actor, initialTab = "hp") {
|
|||||||
{
|
{
|
||||||
width: 720,
|
width: 720,
|
||||||
classes: ["pf1-history-dialog"],
|
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;
|
||||||
}
|
}
|
||||||
).render(true);
|
|
||||||
|
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 = '<i class="fas fa-cog" style="font-size: 18px;"></i>';
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
dialog.render(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildHistoryContent(actor, initialTab = "hp") {
|
function buildHistoryContent(actor, initialTab = "hp") {
|
||||||
@@ -309,7 +416,7 @@ function buildHistoryContent(actor, initialTab = "hp") {
|
|||||||
{ label: "HP", render: (entry) => entry.value },
|
{ label: "HP", render: (entry) => entry.value },
|
||||||
{ label: "Δ", render: (entry) => entry.diff },
|
{ label: "Δ", render: (entry) => entry.diff },
|
||||||
{ label: "User", render: (entry) => entry.user ?? "" },
|
{ 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: "XP", render: (entry) => entry.value },
|
||||||
{ label: "Δ", render: (entry) => entry.diff },
|
{ label: "Δ", render: (entry) => entry.diff },
|
||||||
{ label: "User", render: (entry) => entry.user ?? "" },
|
{ 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: "Totals", render: (entry) => entry.value },
|
||||||
{ label: "Δ", render: (entry) => entry.diff },
|
{ label: "Δ", render: (entry) => entry.diff },
|
||||||
{ label: "User", render: (entry) => entry.user ?? "" },
|
{ 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}
|
${toolbar}
|
||||||
<nav class="history-dialog-tabs">${tabs}</nav>
|
<nav class="history-dialog-tabs">${tabs}</nav>
|
||||||
<div class="history-dialog-panels">${panels}</div>
|
<div class="history-dialog-panels">${panels}</div>
|
||||||
<script type="text/javascript">
|
|
||||||
(function(){
|
|
||||||
const root = document.currentScript.closest('[data-history-root="${actor.id}"]');
|
|
||||||
if (!root) return;
|
|
||||||
const buttons = Array.from(root.querySelectorAll('.history-tab-link'));
|
|
||||||
const panels = Array.from(root.querySelectorAll('.history-panel'));
|
|
||||||
function activate(target) {
|
|
||||||
buttons.forEach((btn) => btn.classList.toggle('active', btn.dataset.historyTab === target));
|
|
||||||
panels.forEach((panel) => (panel.style.display = panel.dataset.historyPanel === target ? 'block' : 'none'));
|
|
||||||
panels.forEach((panel) => panel.classList.toggle('active', panel.dataset.historyPanel === target));
|
|
||||||
}
|
|
||||||
buttons.forEach((btn) => {
|
|
||||||
btn.addEventListener('click', (event) => {
|
|
||||||
event.preventDefault();
|
|
||||||
activate(btn.dataset.historyTab);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
const configBtn = root.querySelector('[data-action="open-config"]');
|
|
||||||
configBtn?.addEventListener('click', (event) => {
|
|
||||||
event.preventDefault();
|
|
||||||
const api = window.GowlersTrackingLedger;
|
|
||||||
if (api?.openConfigForActor) api.openConfigForActor("${actor.id}");
|
|
||||||
else api?.openConfig?.();
|
|
||||||
});
|
|
||||||
activate("${initialTab}");
|
|
||||||
})();
|
|
||||||
</script>
|
|
||||||
</section>`;
|
</section>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -448,6 +540,7 @@ async function recordHistoryEntry(actor, statId, previous, nextValue, userId) {
|
|||||||
diff: config.formatDiff(diffValue),
|
diff: config.formatDiff(diffValue),
|
||||||
user: game.users.get(userId)?.name ?? "System",
|
user: game.users.get(userId)?.name ?? "System",
|
||||||
source: "",
|
source: "",
|
||||||
|
encounterId: game.combat?.id ?? null, // Track which encounter this change occurred in
|
||||||
};
|
};
|
||||||
|
|
||||||
const existing = (await actor.getFlag(FLAG_SCOPE, config.flag)) ?? [];
|
const existing = (await actor.getFlag(FLAG_SCOPE, config.flag)) ?? [];
|
||||||
@@ -641,11 +734,11 @@ class TrackingLedgerConfig extends FormApplication {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
static PAGE_OPTIONS = [25, 50, 100, 250];
|
static PAGE_OPTIONS = [10, 20, "all"];
|
||||||
static DEFAULT_PAGE_SIZE = 50;
|
static DEFAULT_PAGE_SIZE = 10;
|
||||||
static _lastFilter = "";
|
static _lastFilter = "";
|
||||||
static _lastPage = 0;
|
static _lastPage = 0;
|
||||||
static _lastPageSize = 50;
|
static _lastPageSize = 10;
|
||||||
|
|
||||||
constructor(...args) {
|
constructor(...args) {
|
||||||
super(...args);
|
super(...args);
|
||||||
@@ -654,10 +747,12 @@ class TrackingLedgerConfig extends FormApplication {
|
|||||||
this._pageSize = TrackingLedgerConfig._lastPageSize ?? TrackingLedgerConfig.DEFAULT_PAGE_SIZE;
|
this._pageSize = TrackingLedgerConfig._lastPageSize ?? TrackingLedgerConfig.DEFAULT_PAGE_SIZE;
|
||||||
this._pageMeta = { totalPages: 1, hasPrev: false, hasNext: false };
|
this._pageMeta = { totalPages: 1, hasPrev: false, hasNext: false };
|
||||||
this._actorRefs = null;
|
this._actorRefs = null;
|
||||||
|
this._filterDebounceTimer = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
get pageSize() {
|
get pageSize() {
|
||||||
return this._pageSize ?? TrackingLedgerConfig.DEFAULT_PAGE_SIZE;
|
const size = this._pageSize ?? TrackingLedgerConfig.DEFAULT_PAGE_SIZE;
|
||||||
|
return size === "all" ? Infinity : (Number.isFinite(size) ? size : TrackingLedgerConfig.DEFAULT_PAGE_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getData() {
|
async getData() {
|
||||||
@@ -771,17 +866,30 @@ class TrackingLedgerConfig extends FormApplication {
|
|||||||
|
|
||||||
activateListeners(html) {
|
activateListeners(html) {
|
||||||
super.activateListeners(html);
|
super.activateListeners(html);
|
||||||
html.find("[data-filter-input]").on("input", (event) => {
|
const filterInput = html.find("[data-filter-input]");
|
||||||
|
filterInput.on("input", (event) => {
|
||||||
this._filter = event.currentTarget.value ?? "";
|
this._filter = event.currentTarget.value ?? "";
|
||||||
this._page = 0;
|
this._page = 0;
|
||||||
TrackingLedgerConfig._lastFilter = this._filter;
|
TrackingLedgerConfig._lastFilter = this._filter;
|
||||||
TrackingLedgerConfig._lastPage = this._page;
|
TrackingLedgerConfig._lastPage = this._page;
|
||||||
|
|
||||||
|
// Debounce render to preserve focus
|
||||||
|
clearTimeout(this._filterDebounceTimer);
|
||||||
|
this._filterDebounceTimer = setTimeout(() => {
|
||||||
this.render(false);
|
this.render(false);
|
||||||
|
}, 300);
|
||||||
});
|
});
|
||||||
|
// Keep focus on filter input after render
|
||||||
|
filterInput.trigger("focus");
|
||||||
|
|
||||||
html.find("[data-page-size]").on("change", (event) => {
|
html.find("[data-page-size]").on("change", (event) => {
|
||||||
const value = Number(event.currentTarget.value);
|
const value = event.currentTarget.value;
|
||||||
this._pageSize = Number.isFinite(value) && value > 0 ? value : TrackingLedgerConfig.DEFAULT_PAGE_SIZE;
|
if (value === "all") {
|
||||||
|
this._pageSize = "all";
|
||||||
|
} else {
|
||||||
|
const num = Number(value);
|
||||||
|
this._pageSize = Number.isFinite(num) && num > 0 ? num : TrackingLedgerConfig.DEFAULT_PAGE_SIZE;
|
||||||
|
}
|
||||||
TrackingLedgerConfig._lastPageSize = this._pageSize;
|
TrackingLedgerConfig._lastPageSize = this._pageSize;
|
||||||
this._page = 0;
|
this._page = 0;
|
||||||
TrackingLedgerConfig._lastPage = 0;
|
TrackingLedgerConfig._lastPage = 0;
|
||||||
@@ -814,6 +922,87 @@ class TrackingLedgerConfig extends FormApplication {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update encounter summary when combat starts
|
||||||
|
*/
|
||||||
|
async function onCombatStart(combat) {
|
||||||
|
if (!combat) return;
|
||||||
|
for (const combatant of combat.combatants) {
|
||||||
|
const actor = combatant.actor;
|
||||||
|
if (!actor) continue;
|
||||||
|
await updateEncounterSummary(actor, combat, "ongoing");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update encounter summary when combat round changes or turns change
|
||||||
|
*/
|
||||||
|
async function onCombatUpdate(combat) {
|
||||||
|
if (!combat) return;
|
||||||
|
for (const combatant of combat.combatants) {
|
||||||
|
const actor = combatant.actor;
|
||||||
|
if (!actor) continue;
|
||||||
|
await updateEncounterSummary(actor, combat, "ongoing");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finalize encounter summary when combat ends
|
||||||
|
*/
|
||||||
|
async function onCombatEnd(combat) {
|
||||||
|
if (!combat) return;
|
||||||
|
for (const combatant of combat.combatants) {
|
||||||
|
const actor = combatant.actor;
|
||||||
|
if (!actor) continue;
|
||||||
|
await updateEncounterSummary(actor, combat, "ended");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update or create encounter summary for an actor
|
||||||
|
*/
|
||||||
|
async function updateEncounterSummary(actor, combat, status = "ongoing") {
|
||||||
|
if (!actor || !combat) return;
|
||||||
|
|
||||||
|
const existing = (await actor.getFlag(FLAG_SCOPE, ENCOUNTER_FLAG)) ?? [];
|
||||||
|
|
||||||
|
// Find existing entry for this combat
|
||||||
|
let encEntry = existing.find((entry) => entry.encounterID === combat.id);
|
||||||
|
|
||||||
|
if (!encEntry) {
|
||||||
|
encEntry = {
|
||||||
|
encounterID: combat.id,
|
||||||
|
dateCreated: Date.now(),
|
||||||
|
dateUpdated: Date.now(),
|
||||||
|
participants: [],
|
||||||
|
status: status,
|
||||||
|
xpGained: 0,
|
||||||
|
rounds: 0,
|
||||||
|
};
|
||||||
|
existing.unshift(encEntry);
|
||||||
|
} else {
|
||||||
|
encEntry.dateUpdated = Date.now();
|
||||||
|
encEntry.status = status;
|
||||||
|
encEntry.rounds = combat.round || 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update participant list with all combatants
|
||||||
|
const participantIds = new Set();
|
||||||
|
for (const combatant of combat.combatants) {
|
||||||
|
if (combatant.actor) {
|
||||||
|
participantIds.add(combatant.actor.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
encEntry.participants = Array.from(participantIds);
|
||||||
|
|
||||||
|
// Keep recent encounters (max 50)
|
||||||
|
if (existing.length > 50) {
|
||||||
|
existing.splice(50);
|
||||||
|
}
|
||||||
|
|
||||||
|
await actor.setFlag(FLAG_SCOPE, ENCOUNTER_FLAG, existing);
|
||||||
|
}
|
||||||
|
|
||||||
function sendChatNotification(statId, actor, previous, nextValue, entry) {
|
function sendChatNotification(statId, actor, previous, nextValue, entry) {
|
||||||
const config = STAT_CONFIGS[statId];
|
const config = STAT_CONFIGS[statId];
|
||||||
if (!config) return;
|
if (!config) return;
|
||||||
|
|||||||
@@ -1,27 +1,29 @@
|
|||||||
<form class="tracking-ledger-config">
|
<form class="tracking-ledger-config">
|
||||||
<section class="tracking-ledger-controls">
|
<section class="tracking-ledger-controls">
|
||||||
<label class="tracking-ledger-filter">
|
<div style="display: flex; gap: 15px; align-items: center; margin-bottom: 10px; flex-wrap: nowrap;">
|
||||||
|
<label class="tracking-ledger-filter" style="flex: 1; min-width: 200px;">
|
||||||
Filter actors:
|
Filter actors:
|
||||||
<input type="text" data-filter-input placeholder="Type a name..." value="{{filter}}" autocomplete="off" autofocus>
|
<input type="text" data-filter-input placeholder="Type a name..." value="{{filter}}" autocomplete="off" autofocus style="width: 100%;">
|
||||||
</label>
|
</label>
|
||||||
<label class="tracking-ledger-page-size">
|
<label class="tracking-ledger-page-size" style="white-space: nowrap;">
|
||||||
Rows per page:
|
Rows per page:
|
||||||
<select data-page-size>
|
<select data-page-size style="margin-left: 5px;">
|
||||||
{{#each pageOptions}}
|
{{#each pageOptions}}
|
||||||
<option value="{{value}}" {{#if selected}}selected{{/if}}>{{value}}</option>
|
<option value="{{value}}" {{#if selected}}selected{{/if}}>{{#if value}}{{value}}{{else}}All{{/if}}</option>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</select>
|
</select>
|
||||||
</label>
|
</label>
|
||||||
<div class="tracking-ledger-pagination">
|
<div class="tracking-ledger-pagination" style="display: flex; gap: 4px; align-items: center; white-space: nowrap; flex-shrink: 0;">
|
||||||
<button type="button" data-action="page" data-direction="prev" {{#unless hasPrev}}disabled{{/unless}}>« Prev</button>
|
<button type="button" data-action="page" data-direction="prev" {{#unless hasPrev}}disabled{{/unless}} style="padding: 2px 6px;">« Prev</button>
|
||||||
{{#if totalActors}}
|
{{#if totalActors}}
|
||||||
<span>Page {{displayPage}} / {{totalPages}}</span>
|
<span style="font-size: 0.9em; min-width: 80px; text-align: center;">Page {{displayPage}} / {{totalPages}}</span>
|
||||||
{{else}}
|
{{else}}
|
||||||
<span>Page 0 / 0</span>
|
<span style="font-size: 0.9em; min-width: 80px; text-align: center;">Page 0 / 0</span>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
<button type="button" data-action="page" data-direction="next" {{#unless hasNext}}disabled{{/unless}}>Next »</button>
|
<button type="button" data-action="page" data-direction="next" {{#unless hasNext}}disabled{{/unless}} style="padding: 2px 6px;">Next »</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="tracking-ledger-summary">
|
</div>
|
||||||
|
<div class="tracking-ledger-summary" style="font-size: 0.9em; color: #666;">
|
||||||
{{#if totalActors}}
|
{{#if totalActors}}
|
||||||
Showing {{showingFrom}}-{{showingTo}} of {{totalActors}} actors
|
Showing {{showingFrom}}-{{showingTo}} of {{totalActors}} actors
|
||||||
{{else}}
|
{{else}}
|
||||||
|
|||||||
Reference in New Issue
Block a user