- ⚔️
- วิเคราะห์ Synergy ทีม & จุดอ่อน
+ 💥
+ สรุปการต่อสู้ล่าสุด & ดาเมจทีม
- ครอบคลุม 0/7 ธาตุ
-
-
วิเคราะห์การครอบคลุม Weakness Break และความพร้อมของอุปกรณ์ในทีม
-
-
-
-
- 🎯 การทำลายจุดอ่อน (Weakness Break Coverage)
+
+
+ ยังไม่มีข้อมูลการต่อสู้
+
+
-
-
+
+
+ สถิติดาเมจ, DPS และสัดส่วนการทำดาเมจของตัวละครในทีมจากการต่อสู้จริงล่าสุด
+
+
+
+
+
+
+
+
-
+
Break / Super Break
+
+
-
+
+
+
+ 📊 สัดส่วนดาเมจสมาชิกในทีม (Damage Share)
+
+
+
+
+
+
+
- 📊 ความพร้อมของอุปกรณ์และเลเวลเฉลี่ย
+ 🛡️ ความพร้อมของอุปกรณ์และเลเวลเฉลี่ย
-
-
-
-
-
💥
-
-
Damage Analyst (จับดาเมจจากในเกม)
-
ยังไม่มีข้อมูลการต่อสู้ล่าสุด
-
-
-
-
diff --git a/js/app.js b/js/app.js
index 1116f5f..a1bc33a 100644
--- a/js/app.js
+++ b/js/app.js
@@ -73,17 +73,19 @@ const el = {
lineupContainer: document.getElementById('lineup-container'),
toggleCreativeMode: document.getElementById('toggle-creative-mode'),
badgeCreativeMode: document.getElementById('badge-creative-mode'),
- // Team Combat Synergy
- cardTeamSynergy: document.getElementById('card-team-synergy'),
- badgeSynergyCoverage: document.getElementById('badge-synergy-coverage'),
- synergyElementsContainer: document.getElementById('synergy-elements-container'),
+ // Mini Battle Summary & Team Stats
+ cardBattleSummary: document.getElementById('card-battle-summary'),
+ badgeMiniBattleStatus: document.getElementById('badge-mini-battle-status'),
+ miniTotalDmg: document.getElementById('mini-total-dmg'),
+ miniTeamDps: document.getElementById('mini-team-dps'),
+ miniBreakDmg: document.getElementById('mini-break-dmg'),
+ miniBattleTurns: document.getElementById('mini-battle-turns'),
+ miniBattleAvatarsList: document.getElementById('mini-battle-avatars-list'),
+ btnMiniViewFullAnalyst: document.getElementById('btn-mini-view-full-analyst'),
synergyAvgLevel: document.getElementById('synergy-avg-level'),
synergyAvgEidolon: document.getElementById('synergy-avg-eidolon'),
synergyLcReady: document.getElementById('synergy-lc-ready'),
synergyRelicReady: document.getElementById('synergy-relic-ready'),
- synergyDamageBanner: document.getElementById('synergy-damage-banner'),
- dashboardBattleTitle: document.getElementById('dashboard-battle-title'),
- dashboardBattleSummary: document.getElementById('dashboard-battle-summary'),
// Damage Analyst
tabDamageAnalyst: document.getElementById('tab-damage-analyst'),
@@ -1267,20 +1269,112 @@ function renderLineup(lineups) {
}
// ==========================================
-// Team Combat Synergy & Weakness Break Analysis
+// Mini Battle Summary & Team Stats
// ==========================================
-const HSR_ELEMENTS = [
- { key: 'Physical', name: 'Physical', nameTh: 'กายภาพ', color: '#cbd5e1' },
- { key: 'Fire', name: 'Fire', nameTh: 'ไฟ', color: '#ef4444' },
- { key: 'Ice', name: 'Ice', nameTh: 'น้ำแข็ง', color: '#38bdf8' },
- { key: 'Lightning', name: 'Lightning', nameTh: 'สายฟ้า', color: '#c084fc' },
- { key: 'Wind', name: 'Wind', nameTh: 'ลม', color: '#34d399' },
- { key: 'Quantum', name: 'Quantum', nameTh: 'ควอนตัม', color: '#818cf8' },
- { key: 'Imaginary', name: 'Imaginary', nameTh: 'จินตภาพ', color: '#fbbf24' }
-];
+const ELEMENT_COLORS = {
+ Physical: '#cbd5e1',
+ Fire: '#ef4444',
+ Ice: '#38bdf8',
+ Lightning: '#c084fc',
+ Thunder: '#c084fc',
+ Wind: '#34d399',
+ Quantum: '#818cf8',
+ Imaginary: '#fbbf24'
+};
+
+function renderMiniBattleSummary() {
+ if (!el.miniBattleAvatarsList) return;
+ const report = analystState.currentReport;
+
+ if (!report || !report.team_total_damage || !report.avatars || report.avatars.length === 0) {
+ if (el.badgeMiniBattleStatus) {
+ el.badgeMiniBattleStatus.textContent = t('miniNoBattleTitle');
+ el.badgeMiniBattleStatus.style.background = 'rgba(156, 163, 175, 0.12)';
+ el.badgeMiniBattleStatus.style.color = 'var(--text-muted)';
+ el.badgeMiniBattleStatus.style.borderColor = 'rgba(156, 163, 175, 0.25)';
+ }
+ if (el.miniTotalDmg) el.miniTotalDmg.textContent = '-';
+ if (el.miniTeamDps) el.miniTeamDps.textContent = '-';
+ if (el.miniBreakDmg) el.miniBreakDmg.textContent = '-';
+ if (el.miniBattleTurns) el.miniBattleTurns.textContent = '-';
+
+ el.miniBattleAvatarsList.innerHTML = `
+
+
⚔️
+
${t('miniNoBattleTitle')}
+
+ ${t('miniNoBattleDesc')}
+
+
+
+
+
+
+ `;
+ return;
+ }
+
+ // Active Report
+ const isWin = (report.end_status || 'WIN').toUpperCase() === 'WIN';
+ if (el.badgeMiniBattleStatus) {
+ el.badgeMiniBattleStatus.textContent = isWin ? '✅ ชนะการต่อสู้' : `⚠️ สิ้นสุด (${report.end_status})`;
+ el.badgeMiniBattleStatus.style.background = isWin ? 'rgba(52, 211, 153, 0.15)' : 'rgba(239, 68, 68, 0.15)';
+ el.badgeMiniBattleStatus.style.color = isWin ? 'var(--accent-green)' : '#f87171';
+ el.badgeMiniBattleStatus.style.borderColor = isWin ? 'rgba(52, 211, 153, 0.4)' : 'rgba(239, 68, 68, 0.4)';
+ }
+
+ const durationSec = report.cost_time || 0;
+ const teamDps = durationSec > 0 ? (report.team_total_damage / durationSec) : 0;
+ let totalBreakDmg = 0;
+ (report.avatars || []).forEach(a => {
+ totalBreakDmg += (a.total_break_damage || 0);
+ });
+
+ if (el.miniTotalDmg) el.miniTotalDmg.textContent = formatNumber(report.team_total_damage);
+ if (el.miniTeamDps) el.miniTeamDps.textContent = `${formatNumber(Math.round(teamDps))}/s`;
+ if (el.miniBreakDmg) el.miniBreakDmg.textContent = formatNumber(totalBreakDmg);
+ if (el.miniBattleTurns) el.miniBattleTurns.textContent = `${report.total_battle_turns || 0} เทิร์น (${formatDuration(durationSec)})`;
+
+ // Sort avatars by total damage descending
+ const sortedAvatars = [...report.avatars].sort((a, b) => (b.total_damage || 0) - (a.total_damage || 0));
+
+ el.miniBattleAvatarsList.innerHTML = sortedAvatars.map(av => {
+ const meta = getAvatarInfo(av.avatar_id);
+ const elemColor = ELEMENT_COLORS[meta.element] || '#38bdf8';
+ const pct = Math.max(0, Math.min(100, av.damage_percent || 0));
+ const iconUrl = getAvatarIconUrl(av.avatar_id);
+ const rankStr = av.rank !== undefined ? `E${av.rank}` : '';
+ const lvlStr = av.level ? `Lv.${av.level}` : '';
+ const metaSub = [lvlStr, rankStr, translatePath(meta.path)].filter(Boolean).join(' • ');
+
+ return `
+
+
+
+

+
+
${meta.name}
+
${metaSub}
+
+
+
+ ${formatNumber(av.total_damage)}
+ ${pct.toFixed(1)}%
+
+
+
+
+ `;
+ }).join('');
+}
function renderTeamSynergy(lineups) {
- if (!el.synergyElementsContainer) return;
const currentLineup = lineups || state.statusData?.lineups || DEFAULT_LINEUP;
// Extract active character IDs
@@ -1292,22 +1386,12 @@ function renderTeamSynergy(lineups) {
}
}
- // 1. Weakness Break Coverage
- const coveredElements = {};
- const roles = {
- dps: [], // Destruction, Hunt, Erudition
- specialist: [], // Remembrance, Elation
- support: [], // Harmony, Nihility
- sustain: [] // Preservation, Abundance
- };
-
let totalLevel = 0;
let totalEidolon = 0;
let lcCount = 0;
let fullRelicCount = 0;
activeIds.forEach(aid => {
- const meta = getAvatarInfo(aid);
const av = state.fullFreesrData?.avatars?.[aid];
const lvl = av?.level || 80;
const rank = av?.data?.rank ?? (state.avatarsList.find(a => a.avatar_id === aid)?.rank ?? 0);
@@ -1315,25 +1399,6 @@ function renderTeamSynergy(lineups) {
totalLevel += lvl;
totalEidolon += rank;
- // Element coverage
- const rawElem = normalizeElement(meta.rawElement || meta.element);
- if (!coveredElements[rawElem]) {
- coveredElements[rawElem] = [];
- }
- coveredElements[rawElem].push(meta.name);
-
- // Path / Role
- const rawPath = normalizePath(meta.rawPath || meta.path);
- if (['Destruction', 'Hunt', 'Erudition'].includes(rawPath)) {
- roles.dps.push(meta.name);
- } else if (['Remembrance', 'Elation'].includes(rawPath)) {
- roles.specialist.push(meta.name);
- } else if (['Harmony', 'Nihility'].includes(rawPath)) {
- roles.support.push(meta.name);
- } else if (['Preservation', 'Abundance'].includes(rawPath)) {
- roles.sustain.push(meta.name);
- }
-
// LC check
const hasLc = (state.fullFreesrData?.lightcones || []).some(l => l.equip_avatar === aid);
if (hasLc) lcCount++;
@@ -1347,33 +1412,13 @@ function renderTeamSynergy(lineups) {
const avgLevel = activeIds.length ? (totalLevel / count).toFixed(1) : '-';
const avgEidolon = activeIds.length ? `E${(totalEidolon / count).toFixed(1)}` : '-';
- // Render Element Chips
- const coveredCount = Object.keys(coveredElements).length;
- if (el.badgeSynergyCoverage) {
- el.badgeSynergyCoverage.textContent = t('synergyCoverageBadge', { count: coveredCount });
- el.badgeSynergyCoverage.style.color = coveredCount >= 4 ? 'var(--accent-green)' : (coveredCount >= 2 ? 'var(--accent-cyan)' : 'var(--accent-gold)');
- }
-
- el.synergyElementsContainer.innerHTML = HSR_ELEMENTS.map(elem => {
- const isCovered = !!coveredElements[elem.key];
- const providers = coveredElements[elem.key] || [];
- const providerText = isCovered ? providers.join(', ') : t('synergyNoInTeam');
- return `
-
- ${getElementIconHtml(elem.key, 22)}
- ${translateElement(elem.key)}
- ${providerText}
-
- `;
- }).join('');
-
// Metrics
if (el.synergyAvgLevel) el.synergyAvgLevel.textContent = activeIds.length ? `Lv.${avgLevel}` : '-';
if (el.synergyAvgEidolon) el.synergyAvgEidolon.textContent = avgEidolon;
if (el.synergyLcReady) el.synergyLcReady.textContent = activeIds.length ? `${lcCount}/${activeIds.length}` : '-';
if (el.synergyRelicReady) el.synergyRelicReady.textContent = activeIds.length ? `${fullRelicCount}/${activeIds.length}` : '-';
- updateDashboardBattleBanner();
+ renderMiniBattleSummary();
}
// ==========================================
@@ -8070,23 +8115,7 @@ function renderDamageAnalyst() {
}
function updateDashboardBattleBanner() {
- if (!el.dashboardBattleTitle || !el.dashboardBattleSummary) return;
- const report = analystState.currentReport;
- if (!report || !report.team_total_damage) {
- el.dashboardBattleTitle.textContent = "Damage Analyst (จับดาเมจจากในเกม)";
- el.dashboardBattleSummary.textContent = "ยังไม่มีข้อมูลการต่อสู้ล่าสุด — กดเพื่อเปิดหน้า Damage Analyst";
- return;
- }
- const topAvatar = (report.avatars && report.avatars.length > 0)
- ? [...report.avatars].sort((a, b) => b.total_damage - a.total_damage)[0]
- : null;
- const topName = topAvatar ? getAvatarInfo(topAvatar.avatar_id).name : '';
- const topDmgPct = topAvatar ? `${topAvatar.damage_percent.toFixed(0)}%` : '';
-
- el.dashboardBattleTitle.textContent = `💥 ดาเมจรวม ${formatNumber(report.team_total_damage)} (${report.round_cnt} รอบ)`;
- el.dashboardBattleSummary.textContent = topName
- ? `MVP: ${topName} (${topDmgPct} Share) • ${report.total_battle_turns} เทิร์น • เวลา ${formatDuration(report.cost_time)}`
- : `สถานะ: ${report.end_status || 'WIN'} • ${report.total_battle_turns} เทิร์น`;
+ renderMiniBattleSummary();
}
function exportBattleReportJson() {
@@ -8289,8 +8318,8 @@ function setupDamageAnalystEvents() {
});
}
- if (el.synergyDamageBanner) {
- el.synergyDamageBanner.addEventListener('click', () => {
+ if (el.btnMiniViewFullAnalyst) {
+ el.btnMiniViewFullAnalyst.addEventListener('click', () => {
const btn = document.querySelector('.tab-btn[data-tab="tab-damage-analyst"]');
if (btn) btn.click();
});
@@ -8305,6 +8334,7 @@ function setupDamageAnalystEvents() {
}
}
+window.renderMiniBattleSummary = renderMiniBattleSummary;
window.fetchLatestBattleReport = fetchLatestBattleReport;
window.loadDemoBattleReport = loadDemoBattleReport;
window.exportBattleReportJson = exportBattleReportJson;
diff --git a/js/i18n.js b/js/i18n.js
index d89f339..2cc5117 100644
--- a/js/i18n.js
+++ b/js/i18n.js
@@ -374,11 +374,23 @@ export const TRANSLATIONS = {
"btnAllE0S1": "⚡ ปรับ E0S1 ทุกตัว",
"qaGachabaseTitle": "ดึงข้อมูล Gachabase",
"qaGachabaseDesc": "นำเข้าตัวละคร/กรวยแสง เบต้าหรือใหม่ล่าสุด",
+ "miniBattleTitle": "สรุปการต่อสู้ล่าสุด & ดาเมจทีม",
+ "miniBattleSubtitle": "สถิติดาเมจ, DPS และสัดส่วนการทำดาเมจของตัวละครในทีมจากการต่อสู้จริงล่าสุด",
+ "miniTotalDmg": "ดาเมจรวม",
+ "miniTeamDps": "DPS รวม",
+ "miniBreakDmg": "Break / Super Break",
+ "miniBattleTurns": "เทิร์น / เวลา",
+ "miniBreakdownTitle": "สัดส่วนดาเมจสมาชิกในทีม (Damage Share)",
+ "miniViewFullAnalyst": "Damage Analyst →",
+ "miniNoBattleTitle": "ยังไม่มีข้อมูลการต่อสู้ล่าสุด",
+ "miniNoBattleDesc": "เข้าสู้ในเกม (Calyx / Shadow / Boss) เพื่อเริ่มจับสถิติดาเมจแบบเรียลไทม์",
+ "miniBtnFetch": "🔄 ดึงข้อมูลสด",
+ "miniBtnDemo": "⚡ โหลดข้อมูลตัวอย่าง",
"synergyTitle": "วิเคราะห์ Synergy ทีม & จุดอ่อน",
"synergySubtitle": "วิเคราะห์การครอบคลุม Weakness Break, ความสมดุลของบทบาท และความพร้อมของทีม",
"synergyBreakCoverage": "🎯 การทำลายจุดอ่อน (Weakness Break Coverage)",
"synergyRoleBalance": "🛡️ การกระจายบทบาทในทีม (Team Composition & Roles)",
- "synergyEquipmentTitle": "📊 ความพร้อมของอุปกรณ์และเลเวลเฉลี่ย",
+ "synergyEquipmentTitle": "🛡️ ความพร้อมของอุปกรณ์และเลเวลเฉลี่ย",
"synergyAvgLevel": "เลเวลเฉลี่ย",
"synergyAvgEidolon": "Eidolon เฉลี่ย",
"synergyLcEquipped": "สวมกรวยแสง",
@@ -807,11 +819,23 @@ export const TRANSLATIONS = {
"btnGenAndEquip": "Generate & Equip",
"selectFromInventory": "Select from Inventory",
"btnAllE0S1": "⚡ All E0S1",
+ "miniBattleTitle": "Recent Battle & Team Damage",
+ "miniBattleSubtitle": "Live damage stats, DPS, and damage share from the latest combat",
+ "miniTotalDmg": "Total DMG",
+ "miniTeamDps": "Team DPS",
+ "miniBreakDmg": "Break / Super Break",
+ "miniBattleTurns": "Turns / Time",
+ "miniBreakdownTitle": "Team Damage Share",
+ "miniViewFullAnalyst": "Damage Analyst →",
+ "miniNoBattleTitle": "No Recent Battle Recorded",
+ "miniNoBattleDesc": "Fight in-game (Calyx / Shadow / Boss) to track real-time combat stats",
+ "miniBtnFetch": "🔄 Fetch Live",
+ "miniBtnDemo": "⚡ Load Demo Data",
"synergyTitle": "Team Synergy & Weakness Analysis",
"synergySubtitle": "Analyze Weakness Break coverage, role balance, and equipment readiness",
"synergyBreakCoverage": "🎯 Weakness Break Coverage",
"synergyRoleBalance": "🛡️ Team Composition & Role Distribution",
- "synergyEquipmentTitle": "📊 Equipment Readiness & Average Level",
+ "synergyEquipmentTitle": "🛡️ Equipment Readiness & Average Level",
"synergyAvgLevel": "Avg Level",
"synergyAvgEidolon": "Avg Eidolon",
"synergyLcEquipped": "Lightcones Equipped",