Planner: Info-Panel + Fight-Namen Fix

- Info-Panel in Sidebar: Farbschema-Legende + prägnante Hinweise bei fehlenden Jobs/Zuordnungen
- fight.php: immer englischen Endpoint nutzen damit Fight-Namen sprachunabhängig stabil sind

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
xziino 2026-05-22 14:59:11 +02:00
parent 86d2a106bb
commit 9a2c1490de
4 changed files with 107 additions and 3 deletions

View File

@ -75,8 +75,9 @@ function localized_graphql_uri(string $language): string {
return preg_replace('#https://[^/]+#', 'https://' . $host, GRAPHQL_URI); return preg_replace('#https://[^/]+#', 'https://' . $host, GRAPHQL_URI);
} }
$acceptLanguage = $language === 'jp' ? 'ja' : $language; // Fight names must be stable regardless of language — always use the English endpoint.
$ch = curl_init(localized_graphql_uri($language)); // Localization only matters for ability/player names in analysis.php.
$ch = curl_init(GRAPHQL_URI);
curl_setopt_array($ch, [ curl_setopt_array($ch, [
CURLOPT_POST => true, CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $payload, CURLOPT_POSTFIELDS => $payload,
@ -84,7 +85,6 @@ curl_setopt_array($ch, [
CURLOPT_HTTPHEADER => [ CURLOPT_HTTPHEADER => [
'Content-Type: application/json', 'Content-Type: application/json',
'Authorization: Bearer ' . $_SESSION['access_token'], 'Authorization: Bearer ' . $_SESSION['access_token'],
'Accept-Language: ' . $acceptLanguage,
], ],
CURLOPT_SSL_VERIFYPEER => !DEV_MODE, CURLOPT_SSL_VERIFYPEER => !DEV_MODE,
]); ]);

View File

@ -451,6 +451,60 @@
font-style: italic; font-style: italic;
} }
/* ── Info Panel ──────────────────────────────────────────────────────────────── */
.planner-info-panel {
margin-top: 14px;
padding-top: 14px;
border-top: 1px solid var(--border);
}
.info-section { margin-bottom: 12px; }
.info-section:last-child { margin-bottom: 0; }
.info-section-title {
font-size: 11px;
color: var(--t3);
text-transform: uppercase;
letter-spacing: 0.06em;
margin-bottom: 8px;
}
.info-legend-items {
display: flex;
flex-direction: column;
gap: 5px;
}
.info-legend-row {
display: flex;
align-items: center;
gap: 8px;
}
.info-legend-dot {
width: 10px;
height: 10px;
border-radius: 2px;
flex-shrink: 0;
}
.info-legend-dot--buff { background: rgba(200,168,75,.8); }
.info-legend-dot--debuff { background: var(--red); }
.info-legend-dot--shield { background: var(--blue); }
.info-legend-label {
font-size: 12px;
color: var(--t2);
}
.info-section--warnings { margin-top: 12px; }
.info-warning {
font-size: 12px;
padding: 3px 0;
}
.info-warning--job { color: var(--t2); }
.info-warning--missing { color: var(--red); }
/* ── Folder Sidebar ──────────────────────────────────────────────────────────── */ /* ── Folder Sidebar ──────────────────────────────────────────────────────────── */
.folder-section { margin-bottom: 2px; } .folder-section { margin-bottom: 2px; }

View File

@ -286,6 +286,7 @@ function renderPlanDetail(plan) {
if (!plan) { if (!plan) {
noplan.style.display = ''; noplan.style.display = '';
content.style.display = 'none'; content.style.display = 'none';
renderInfoPanel(null);
return; return;
} }
@ -325,6 +326,7 @@ function renderPlanDetail(plan) {
}); });
initJobSlots(plan.id); initJobSlots(plan.id);
initMechanicClicks(plan.id); initMechanicClicks(plan.id);
renderInfoPanel(plan);
} }
function renderMechanicListHtml(plan) { function renderMechanicListHtml(plan) {
@ -438,6 +440,52 @@ function refreshMechanicList(planId) {
if (!plan) return; if (!plan) return;
const el = document.getElementById('mechanic-list'); const el = document.getElementById('mechanic-list');
if (el) el.innerHTML = renderMechanicListHtml(plan); if (el) el.innerHTML = renderMechanicListHtml(plan);
renderInfoPanel(plan);
}
function renderInfoPanel(plan) {
const el = document.getElementById('planner-info-panel');
if (!el) return;
const legendHtml = `
<div class="info-section">
<div class="info-section-title">Farbschema</div>
<div class="info-legend-items">
<div class="info-legend-row"><span class="info-legend-dot info-legend-dot--buff"></span><span class="info-legend-label">Mitigation</span></div>
<div class="info-legend-row"><span class="info-legend-dot info-legend-dot--debuff"></span><span class="info-legend-label">Debuff</span></div>
<div class="info-legend-row"><span class="info-legend-dot info-legend-dot--shield"></span><span class="info-legend-label">Schild</span></div>
</div>
</div>`;
if (!plan) { el.innerHTML = legendHtml; return; }
const activeJobSet = new Set(plan.jobComposition.filter(j => j));
const noJobAbilities = new Set();
const missingJobPairs = new Set();
for (const m of plan.mechanics) {
for (const a of m.assignments) {
if (!a.job) {
noJobAbilities.add(a.ability);
} else if (!activeJobSet.has(a.job)) {
missingJobPairs.add(`${a.job} · ${a.ability}`);
}
}
}
let warningsHtml = '';
if (noJobAbilities.size > 0 || missingJobPairs.size > 0) {
warningsHtml = `<div class="info-section info-section--warnings"><div class="info-section-title">Hinweise</div>`;
if (noJobAbilities.size > 0) {
warningsHtml += `<div class="info-warning info-warning--job">Fähigkeiten ohne Job-Zuordnung</div>`;
}
if (missingJobPairs.size > 0) {
warningsHtml += `<div class="info-warning info-warning--missing">Fähigkeiten mit fehlendem Job</div>`;
}
warningsHtml += '</div>';
}
el.innerHTML = legendHtml + warningsHtml;
} }
function removeAssignment(planId, mechanicId, abilityName) { function removeAssignment(planId, mechanicId, abilityName) {

View File

@ -25,6 +25,8 @@
</div> </div>
<div id="plan-list"></div> <div id="plan-list"></div>
<div id="planner-info-panel" class="planner-info-panel"></div>
</div> </div>
<!-- Right: Plan detail --> <!-- Right: Plan detail -->