- Add adminMode toggle in AdminPanel (green "Admin ON/OFF" button) - Pass adminMode through GameApp → PuzzleView - Show purple "🛠 Resolver" button in puzzle toolbar when admin is active - Auto-solve gives 3 stars instantly and shows completion overlay - Lets admin skip through all 96 levels for rapid testing Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
125 lines
4.5 KiB
JavaScript
125 lines
4.5 KiB
JavaScript
/**
|
|
* AdminPanel.jsx — Debug/admin panel for SynthQuest
|
|
* Allows adding/removing stars and unlocking levels for testing
|
|
*/
|
|
import React, { useState } from 'react';
|
|
import { loadProgress, saveProgress, resetProgress } from './gameState.js';
|
|
|
|
export default function AdminPanel({ worlds, onClose, adminMode, onToggleAdmin }) {
|
|
const [, refresh] = useState(0);
|
|
const p = loadProgress();
|
|
const totalStars = Object.values(p.completedLevels).reduce((s, l) => s + (l.stars || 0), 0);
|
|
|
|
const setStars = (levelId, stars) => {
|
|
if (stars <= 0) {
|
|
delete p.completedLevels[levelId];
|
|
} else {
|
|
p.completedLevels[levelId] = { stars: Math.min(3, stars), completedAt: Date.now() };
|
|
}
|
|
saveProgress();
|
|
refresh(n => n + 1);
|
|
};
|
|
|
|
const unlockWorld = (world) => {
|
|
// Give 1 star to each level in all previous worlds up to the requirement
|
|
let needed = world.unlockStars || 0;
|
|
for (const w of worlds) {
|
|
if (w.id === world.id) break;
|
|
for (const level of w.levels) {
|
|
if (needed <= 0) break;
|
|
const existing = p.completedLevels[level.id]?.stars || 0;
|
|
if (existing < 1) {
|
|
p.completedLevels[level.id] = { stars: 1, completedAt: Date.now() };
|
|
needed -= 1;
|
|
}
|
|
}
|
|
}
|
|
saveProgress();
|
|
refresh(n => n + 1);
|
|
};
|
|
|
|
const giveAllStars = () => {
|
|
for (const w of worlds) {
|
|
for (const level of w.levels) {
|
|
p.completedLevels[level.id] = { stars: 3, completedAt: Date.now() };
|
|
}
|
|
}
|
|
saveProgress();
|
|
refresh(n => n + 1);
|
|
};
|
|
|
|
const handleReset = () => {
|
|
resetProgress();
|
|
refresh(n => n + 1);
|
|
};
|
|
|
|
return (
|
|
<div className="admin-overlay" onClick={onClose}>
|
|
<div className="admin-panel" onClick={e => e.stopPropagation()}>
|
|
<div className="admin-header">
|
|
<h2>🛠 Admin Mode</h2>
|
|
<span className="admin-total">Total: ★ {totalStars}</span>
|
|
<button className="admin-close" onClick={onClose}>✕</button>
|
|
</div>
|
|
|
|
<div className="admin-actions">
|
|
<button
|
|
className={`admin-action-btn ${adminMode ? 'active' : ''}`}
|
|
onClick={onToggleAdmin}
|
|
>
|
|
{adminMode ? '🛠 Admin ON' : '🛠 Admin OFF'}
|
|
</button>
|
|
<button className="admin-action-btn gold" onClick={giveAllStars}>★★★ Todo</button>
|
|
<button className="admin-action-btn danger" onClick={handleReset}>Reset Progreso</button>
|
|
</div>
|
|
|
|
<div className="admin-worlds">
|
|
{worlds.map((world, wi) => {
|
|
const worldStars = world.levels.reduce((s, l) => {
|
|
return s + (p.completedLevels[l.id]?.stars || 0);
|
|
}, 0);
|
|
const isUnlocked = !world.unlockStars || totalStars >= world.unlockStars;
|
|
|
|
return (
|
|
<div key={world.id} className="admin-world">
|
|
<div className="admin-world-header">
|
|
<span className="admin-world-icon" style={{ color: world.color }}>{world.icon}</span>
|
|
<span className="admin-world-name">M{wi + 1}: {world.name}</span>
|
|
<span className="admin-world-stars">★ {worldStars}/{world.levels.length * 3}</span>
|
|
{!isUnlocked && (
|
|
<button className="admin-unlock-btn" onClick={() => unlockWorld(world)}>
|
|
🔓 Desbloquear
|
|
</button>
|
|
)}
|
|
</div>
|
|
<div className="admin-levels">
|
|
{world.levels.map((level, li) => {
|
|
const stars = p.completedLevels[level.id]?.stars || 0;
|
|
return (
|
|
<div key={level.id} className="admin-level">
|
|
<span className="admin-level-num">{wi + 1}.{li + 1}</span>
|
|
<span className="admin-level-name">{level.title}</span>
|
|
<div className="admin-star-btns">
|
|
{[0, 1, 2, 3].map(s => (
|
|
<button
|
|
key={s}
|
|
className={`admin-star-btn ${stars >= s && s > 0 ? 'active' : ''} ${s === 0 ? 'zero' : ''}`}
|
|
onClick={() => setStars(level.id, s)}
|
|
>
|
|
{s === 0 ? '✕' : '★'.repeat(s)}
|
|
</button>
|
|
))}
|
|
</div>
|
|
</div>
|
|
);
|
|
})}
|
|
</div>
|
|
</div>
|
|
);
|
|
})}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|