PlotDirector/PlotLine/wwwroot/js/story-intelligence-progress.js

79 lines
3.3 KiB
JavaScript

(() => {
const progressRoots = [...document.querySelectorAll("[data-story-intelligence-progress]")];
const dashboardRoots = [...document.querySelectorAll("[data-story-intelligence-dashboard]")];
const roots = [...progressRoots, ...dashboardRoots];
if (roots.length === 0 || !window.signalR) {
return;
}
const field = (state, camel, pascal) => state?.[camel] ?? state?.[pascal] ?? null;
const jobIds = [...new Set(roots.map((root) => root.dataset.storyIntelligenceJobId).filter(Boolean))];
const updateRoot = (root, state) => {
const percent = field(state, "progressPercent", "ProgressPercent") ?? 0;
const status = field(state, "status", "Status") || "Pending";
const stage = field(state, "currentStage", "CurrentStage") || status;
const message = field(state, "currentMessage", "CurrentMessage") || "Story Intelligence is preparing.";
const elapsed = field(state, "elapsedTime", "ElapsedTime") || "0 sec";
const remaining = field(state, "estimatedRemaining", "EstimatedRemaining") || "Calculating";
root.querySelectorAll("[data-story-intelligence-message]").forEach((node) => {
node.textContent = message;
});
root.querySelectorAll("[data-story-intelligence-percent]").forEach((node) => {
node.textContent = `${percent}%`;
});
root.querySelectorAll("[data-story-intelligence-progress-bar]").forEach((node) => {
node.style.width = `${Math.max(0, Math.min(100, Number.parseInt(percent, 10) || 0))}%`;
});
root.querySelectorAll("[data-story-intelligence-stage]").forEach((node) => {
node.textContent = stage;
});
root.querySelectorAll("[data-story-intelligence-status]").forEach((node) => {
node.textContent = status;
});
root.querySelectorAll("[data-story-intelligence-elapsed]").forEach((node) => {
node.textContent = elapsed;
});
root.querySelectorAll("[data-story-intelligence-remaining]").forEach((node) => {
node.textContent = remaining;
});
const completed = !!field(state, "isCompleted", "IsCompleted");
root.querySelectorAll("[data-story-intelligence-complete-link]").forEach((node) => {
node.classList.toggle("disabled", !completed);
node.setAttribute("aria-disabled", String(!completed));
});
};
const applyProgress = (state) => {
const jobId = field(state, "jobID", "JobID") || field(state, "jobId", "JobId");
if (!jobId) {
return;
}
roots
.filter((root) => root.dataset.storyIntelligenceJobId?.toLowerCase() === String(jobId).toLowerCase())
.forEach((root) => updateRoot(root, state));
if (progressRoots.length > 0 && field(state, "isCompleted", "IsCompleted")) {
window.location.href = `/onboarding/story-intelligence/complete?jobId=${encodeURIComponent(jobId)}`;
}
};
const connection = new signalR.HubConnectionBuilder()
.withUrl("/hubs/story-intelligence")
.withAutomaticReconnect()
.build();
connection.on("StoryIntelligenceProgressChanged", applyProgress);
connection.onreconnected(() => {
jobIds.forEach((jobId) => connection.invoke("WatchStoryIntelligenceJob", jobId).then(applyProgress).catch(() => {}));
});
connection.start()
.then(() => Promise.all(jobIds.map((jobId) => connection.invoke("WatchStoryIntelligenceJob", jobId))))
.then((states) => states.filter(Boolean).forEach(applyProgress))
.catch(() => {});
})();