refactor: rediseñar tablero de cartas en sentido horizontal, añadiendo zoom y lupa cuando hay muchas cartas para poder ver toda la escala y tener control al mismo tiempo.
This commit is contained in:
@@ -1,13 +1,14 @@
|
|||||||
export default function AddLevelButton({ handleAddLevel }) {
|
export default function AddLevelButton({ handleAddLevel }) {
|
||||||
return (
|
return (
|
||||||
<div className="w-72 mt-6">
|
<div className="flex flex-col items-center mx-2 my-4">
|
||||||
<button
|
<button
|
||||||
onClick={handleAddLevel}
|
onClick={handleAddLevel}
|
||||||
className="w-full h-24 border-4 border-dashed border-slate-300 rounded-xl flex flex-col items-center justify-center gap-1 text-slate-400 font-semibold hover:bg-blue-50 hover:border-blue-400 hover:text-blue-500 transition-all active:scale-[0.98]"
|
className="w-44 h-60 border-4 border-dashed border-slate-300 rounded-2xl flex flex-col items-center justify-center gap-2 text-slate-400 font-semibold hover:bg-blue-50 hover:border-blue-400 hover:text-blue-500 transition-all active:scale-[0.98] group"
|
||||||
>
|
>
|
||||||
<span className="text-3xl leading-none">+</span>
|
<span className="text-4xl font-light leading-none group-hover:scale-110 transition-transform">+</span>
|
||||||
<span className="text-sm">Añadir Carta</span>
|
<span className="text-sm uppercase tracking-widest font-bold text-center px-4">Añadir Carta</span>
|
||||||
</button>
|
</button>
|
||||||
|
<div className="h-6 mt-3"></div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -1,36 +1,40 @@
|
|||||||
export default function BlankCardsCounter({ index, blankCardsCount, handleBlankCardChange }) {
|
export default function BlankCardsCounter({ index, blankCardsCount, handleBlankCardChange }) {
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col items-center my-2 w-full">
|
<div className="flex flex-col items-center relative mx-1 mb-10">
|
||||||
<div className="w-0.5 h-6 bg-slate-300"></div>
|
|
||||||
|
|
||||||
<div className="flex items-center gap-3 bg-white px-4 py-2 rounded-full shadow-sm border border-slate-200 z-10">
|
<div className="absolute w-full h-1 bg-slate-200 top-[40%] -translate-y-1/2 -z-10 rounded"></div>
|
||||||
<span className="text-xs font-bold text-slate-400 uppercase tracking-wider">Blancas:</span>
|
|
||||||
|
{/* Botones de - y + */}
|
||||||
|
<div className="flex items-center gap-1 bg-white px-2 py-1.5 rounded-full shadow-sm border border-slate-200 z-10">
|
||||||
<button
|
<button
|
||||||
onClick={() => handleBlankCardChange(index, -1)}
|
onClick={() => handleBlankCardChange(index, -1)}
|
||||||
className="w-7 h-7 flex items-center justify-center rounded-full bg-slate-100 hover:bg-slate-200 text-slate-600 font-bold transition-colors"
|
className="w-7 h-7 flex items-center justify-center rounded-full bg-slate-50 hover:bg-slate-200 text-slate-600 font-bold transition-colors"
|
||||||
>-</button>
|
>-</button>
|
||||||
<span className="text-base font-bold w-6 text-center text-blue-600">
|
|
||||||
{blankCardsCount}
|
<div className="flex flex-col items-center leading-none min-w-[3rem]">
|
||||||
</span>
|
<span className="text-[9px] font-bold text-slate-400 uppercase tracking-widest mb-1">Blancas</span>
|
||||||
|
<span className="text-base font-black text-blue-600">{blankCardsCount}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
onClick={() => handleBlankCardChange(index, 1)}
|
onClick={() => handleBlankCardChange(index, 1)}
|
||||||
className="w-7 h-7 flex items-center justify-center rounded-full bg-slate-100 hover:bg-slate-200 text-slate-600 font-bold transition-colors"
|
className="w-7 h-7 flex items-center justify-center rounded-full bg-slate-50 hover:bg-slate-200 text-slate-600 font-bold transition-colors"
|
||||||
>+</button>
|
>+</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Cartas blancas */}
|
||||||
{blankCardsCount > 0 && (
|
{blankCardsCount > 0 && (
|
||||||
<div className="flex flex-col items-center -space-y-4 mt-3 mb-1">
|
<div className="absolute top-[75px] flex flex-row items-center -space-x-4">
|
||||||
{Array.from({ length: blankCardsCount }).map((_, i) => (
|
{Array.from({ length: blankCardsCount }).map((_, i) => (
|
||||||
<div
|
<div
|
||||||
key={i}
|
key={i}
|
||||||
className="w-16 h-8 bg-blue-50 border-2 border-blue-200 rounded shadow-sm opacity-80"
|
className="w-8 h-12 bg-white border-2 border-dashed border-slate-300 rounded shadow-sm opacity-90 transition-all hover:-translate-y-1"
|
||||||
style={{ zIndex: i }}
|
style={{ zIndex: i }}
|
||||||
></div>
|
></div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="w-0.5 h-6 bg-slate-300"></div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -1,10 +1,9 @@
|
|||||||
export default function CardEditor({ index, level, handleLevelChange, handleRemoveLevel, totalLevels, error }) {
|
export default function CardEditor({ index, level, handleLevelChange, handleRemoveLevel, totalLevels, error }) {
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col items-center">
|
<div className="flex flex-col items-center mx-2 my-4">
|
||||||
<div className={`relative w-72 h-44 bg-white border-2 rounded-xl shadow-[0_8px_30px_rgb(0,0,0,0.08)] flex flex-col items-center justify-center transition-transform hover:-translate-y-1 hover:shadow-[0_12px_40px_rgb(0,0,0,0.12)] group ${
|
<div className={`relative w-44 h-60 bg-white border-2 rounded-2xl shadow-[0_8px_30px_rgb(0,0,0,0.08)] flex flex-col items-center justify-center transition-transform hover:-translate-y-2 hover:shadow-[0_12px_40px_rgb(0,0,0,0.12)] group ${
|
||||||
error ? 'border-red-400 shadow-red-100' : 'border-slate-200'
|
error ? 'border-red-400 shadow-red-100' : 'border-slate-200'
|
||||||
}`}>
|
}`}>
|
||||||
{/* Botón para eliminar */}
|
|
||||||
{totalLevels > 3 && (
|
{totalLevels > 3 && (
|
||||||
<button
|
<button
|
||||||
onClick={() => handleRemoveLevel(index)}
|
onClick={() => handleRemoveLevel(index)}
|
||||||
@@ -15,30 +14,27 @@ export default function CardEditor({ index, level, handleLevelChange, handleRemo
|
|||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Detalles tipo naipe */}
|
<span className="absolute top-3 left-4 text-sm font-black text-slate-300">{index + 1}</span>
|
||||||
<span className="absolute top-4 left-4 text-sm font-black text-slate-300">{index + 1}</span>
|
<span className="absolute bottom-3 right-4 text-sm font-black text-slate-300 rotate-180">{index + 1}</span>
|
||||||
<span className="absolute bottom-4 right-4 text-sm font-black text-slate-300 rotate-180">{index + 1}</span>
|
|
||||||
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="Escribe aquí..."
|
placeholder="Etiqueta..."
|
||||||
value={level}
|
value={level}
|
||||||
onChange={(e) => handleLevelChange(index, e.target.value)}
|
onChange={(e) => handleLevelChange(index, e.target.value)}
|
||||||
className={`w-4/5 text-center text-2xl font-bold text-slate-700 bg-transparent border-b-2 border-dashed outline-none pb-1 ${
|
className={`w-10/12 text-center text-xl font-bold text-slate-700 bg-transparent border-b-2 border-dashed outline-none pb-1 ${
|
||||||
error ? 'border-red-300 focus:border-red-500 placeholder:text-red-200' : 'border-slate-300 focus:border-blue-500'
|
error ? 'border-red-300 focus:border-red-500 placeholder:text-red-200' : 'border-slate-300 focus:border-blue-500'
|
||||||
}`}
|
}`}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Mensaje de error */}
|
<div className="h-6 mt-3">
|
||||||
<div className="h-6 mt-2">
|
|
||||||
{error && (
|
{error && (
|
||||||
<p className="text-red-500 text-sm font-semibold animate-pulse">
|
<p className="text-red-500 text-sm font-semibold animate-pulse">
|
||||||
Escribe una etiqueta
|
Escribe una etiqueta
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -19,6 +19,9 @@ export default function AdvancedMode() {
|
|||||||
const [blankCards, setBlankCards] = useState([0, 0]);
|
const [blankCards, setBlankCards] = useState([0, 0]);
|
||||||
const [errors, setErrors] = useState({ criterion: false, levels: [] });
|
const [errors, setErrors] = useState({ criterion: false, levels: [] });
|
||||||
|
|
||||||
|
// Estado para controlar la lupa (Zoom)
|
||||||
|
const [isZoomActive, setIsZoomActive] = useState(true);
|
||||||
|
|
||||||
// Estados Fase 2 (Franjas)
|
// Estados Fase 2 (Franjas)
|
||||||
const [baseScale, setBaseScale] = useState({});
|
const [baseScale, setBaseScale] = useState({});
|
||||||
const [selectedTerm, setSelectedTerm] = useState(null);
|
const [selectedTerm, setSelectedTerm] = useState(null);
|
||||||
@@ -94,31 +97,68 @@ export default function AdvancedMode() {
|
|||||||
alert("¡Mira la consola! JSON preparado.");
|
alert("¡Mira la consola! JSON preparado.");
|
||||||
};
|
};
|
||||||
|
|
||||||
// Variables calculadas
|
|
||||||
const scaleKeys = Object.keys(baseScale);
|
const scaleKeys = Object.keys(baseScale);
|
||||||
const selectedColor = COLORS[scaleKeys.indexOf(selectedTerm) % COLORS.length] || '#2563eb';
|
const selectedColor = COLORS[scaleKeys.indexOf(selectedTerm) % COLORS.length] || '#2563eb';
|
||||||
|
|
||||||
|
const totalElements = levels.length;
|
||||||
|
const dynamicScale = totalElements > 6 ? 6.2 / totalElements : 1;
|
||||||
|
const currentScale = isZoomActive ? Math.max(0.4, dynamicScale) : 1;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-full flex flex-col items-center">
|
<div className="w-full flex flex-col items-center">
|
||||||
|
|
||||||
{/* --- PASO 1 --- */}
|
{/* PASO 1 */}
|
||||||
{step === 1 && (
|
{step === 1 && (
|
||||||
<div className="w-full bg-white p-8 rounded-3xl shadow-sm border border-slate-200 mb-12 flex flex-col items-center animate-fade-in">
|
<div className="w-full bg-white p-8 rounded-3xl shadow-sm border border-slate-200 mb-12 flex flex-col items-center animate-fade-in overflow-hidden">
|
||||||
<h2 className="text-2xl font-bold text-slate-800 mb-8 w-full border-b pb-4">Paso 1: Escala de Referencia (Cartas)</h2>
|
|
||||||
|
<div className="flex justify-between items-center w-full mb-8 border-b pb-4">
|
||||||
|
<h2 className="text-2xl font-bold text-slate-800">
|
||||||
|
Paso 1: Escala de Referencia (Mesa)
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
{totalElements > 6 && (
|
||||||
|
<button
|
||||||
|
onClick={() => setIsZoomActive(!isZoomActive)}
|
||||||
|
className={`flex items-center gap-2 px-4 py-2 rounded-xl font-bold transition-all shadow-sm border-2 ${
|
||||||
|
isZoomActive
|
||||||
|
? 'bg-blue-50 border-blue-200 text-blue-700 hover:bg-blue-100'
|
||||||
|
: 'bg-white border-slate-200 text-slate-600 hover:bg-slate-50'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<span className="text-xl">{isZoomActive ? '🔍' : '🖼️'}</span>
|
||||||
|
{isZoomActive ? 'Ver de cerca' : 'Ajustar mesa a la pantalla'}
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
<CriterionInput criterionName={criterionName} setCriterionName={handleCriterionChange} error={errors.criterion} />
|
<CriterionInput criterionName={criterionName} setCriterionName={handleCriterionChange} error={errors.criterion} />
|
||||||
|
|
||||||
<div className="w-full max-w-lg flex flex-col items-center">
|
<div className={`w-full mt-6 flex ${!isZoomActive ? 'overflow-x-auto pb-12 justify-start px-4' : 'overflow-hidden justify-center'}`}>
|
||||||
{levels.map((level, index) => (
|
|
||||||
<div key={index} className="w-full flex flex-col items-center">
|
<div
|
||||||
<CardEditor index={index} level={level} handleLevelChange={handleLevelChange} handleRemoveLevel={handleRemoveLevel} totalLevels={levels.length} error={errors.levels[index]} canRemove={levels.length > 3} />
|
className={`flex flex-row items-center min-w-max px-2 transition-transform duration-500 ease-out ${isZoomActive ? 'origin-top' : 'origin-top-left'}`}
|
||||||
{index < levels.length - 1 && <BlankCardsCounter index={index} blankCardsCount={blankCards[index]} handleBlankCardChange={handleBlankCardChange} />}
|
style={{
|
||||||
</div>
|
transform: `scale(${currentScale})`,
|
||||||
))}
|
marginBottom: isZoomActive && currentScale < 1 ? `-${(1 - currentScale) * 350}px` : '0px'
|
||||||
<AddLevelButton handleAddLevel={handleAddLevel} />
|
}}
|
||||||
|
>
|
||||||
|
{levels.map((level, index) => (
|
||||||
|
<React.Fragment key={index}>
|
||||||
|
<CardEditor index={index} level={level} handleLevelChange={handleLevelChange} handleRemoveLevel={handleRemoveLevel} totalLevels={levels.length} error={errors.levels[index]} canRemove={levels.length > 3} />
|
||||||
|
|
||||||
|
{index < levels.length - 1 && (
|
||||||
|
<BlankCardsCounter index={index} blankCardsCount={blankCards[index]} handleBlankCardChange={handleBlankCardChange} />
|
||||||
|
)}
|
||||||
|
</React.Fragment>
|
||||||
|
))}
|
||||||
|
|
||||||
|
<div className="w-6 h-1 bg-slate-200 mx-1 top-[40%]"></div>
|
||||||
|
<AddLevelButton handleAddLevel={handleAddLevel} />
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="w-full max-w-lg mt-12 pt-8 border-t-2 border-slate-200 flex flex-col items-center">
|
<div className="w-full max-w-lg mt-12 pt-8 border-t-2 border-slate-200 flex flex-col items-center z-20 relative bg-white">
|
||||||
<button onClick={handleGenerateBaseScale} disabled={isLoading} className={`w-full py-4 text-white text-xl font-bold rounded-xl shadow-lg transition-all active:scale-[0.98] ${isLoading ? 'bg-slate-400 cursor-not-allowed' : 'bg-gradient-to-r from-blue-600 to-indigo-600 hover:from-blue-700 hover:to-indigo-700'}`}>
|
<button onClick={handleGenerateBaseScale} disabled={isLoading} className={`w-full py-4 text-white text-xl font-bold rounded-xl shadow-lg transition-all active:scale-[0.98] ${isLoading ? 'bg-slate-400 cursor-not-allowed' : 'bg-gradient-to-r from-blue-600 to-indigo-600 hover:from-blue-700 hover:to-indigo-700'}`}>
|
||||||
{isLoading ? 'Calculando...' : 'Generar Gráfica Continua'}
|
{isLoading ? 'Calculando...' : 'Generar Gráfica Continua'}
|
||||||
</button>
|
</button>
|
||||||
@@ -126,7 +166,7 @@ export default function AdvancedMode() {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* --- PASO 2 --- */}
|
{/* PASO 2 */}
|
||||||
{step === 2 && (
|
{step === 2 && (
|
||||||
<div className="w-full max-w-6xl bg-white p-10 rounded-3xl shadow-sm border border-slate-200 animate-fade-in">
|
<div className="w-full max-w-6xl bg-white p-10 rounded-3xl shadow-sm border border-slate-200 animate-fade-in">
|
||||||
<div className="flex justify-between items-center mb-8 border-b pb-4">
|
<div className="flex justify-between items-center mb-8 border-b pb-4">
|
||||||
|
|||||||
Reference in New Issue
Block a user