feat(transform): passo de escolha entre 4 posições (0/90/180/270) antes do ajuste fino
continuous-integration/webhook Deploy concluído (VPS4)
continuous-integration/webhook Deploy concluído (VPS4)
Ao clicar em Orientar, primeiro mostra 4 rotações da imagem para escolher a orientação grossa; ao clicar numa, abre o ajuste fino (escolhida + original) com botão para voltar. Rotação salva = orientação escolhida + ajuste fino.
This commit is contained in:
@@ -14,6 +14,8 @@ function formatDate(dateString) {
|
||||
export default function TransformModal({ isOpen, onClose, image, onSaved }) {
|
||||
const { showToast } = useToast();
|
||||
const [activeView, setActiveView] = useState('transform'); // 'transform' | 'gto'
|
||||
const [step, setStep] = useState('choose'); // 'choose' (4 posições) | 'finetune'
|
||||
const [baseRotation, setBaseRotation] = useState(0); // 0 | 90 | 180 | 270
|
||||
const [showInfo, setShowInfo] = useState(false);
|
||||
const [fineTuneAngle, setFineTuneAngle] = useState(0);
|
||||
const [remark, setRemark] = useState('');
|
||||
@@ -29,6 +31,8 @@ export default function TransformModal({ isOpen, onClose, image, onSaved }) {
|
||||
useEffect(() => {
|
||||
if (!image) return;
|
||||
setFineTuneAngle(0);
|
||||
setBaseRotation(0);
|
||||
setStep('choose');
|
||||
setRemark('');
|
||||
setShowInfo(false);
|
||||
setActiveView('transform');
|
||||
@@ -70,15 +74,17 @@ export default function TransformModal({ isOpen, onClose, image, onSaved }) {
|
||||
|
||||
const resetAngle = () => setFineTuneAngle(0);
|
||||
|
||||
const totalRotation = ((baseRotation + fineTuneAngle) % 360 + 360) % 360;
|
||||
|
||||
const saveTransform = async () => {
|
||||
if (fineTuneAngle === 0) {
|
||||
if (totalRotation === 0) {
|
||||
showToast('Nenhuma edição aplicada.', 'error');
|
||||
return;
|
||||
}
|
||||
setSaving(true);
|
||||
try {
|
||||
await api.post(`/images/${image.id}/transform`, {
|
||||
rotation: fineTuneAngle,
|
||||
rotation: totalRotation,
|
||||
flipH: false,
|
||||
flipV: false,
|
||||
remark
|
||||
@@ -180,8 +186,44 @@ export default function TransformModal({ isOpen, onClose, image, onSaved }) {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* View: Transformação */}
|
||||
{activeView === 'transform' && (
|
||||
{/* View: Transformação — PASSO 1: escolher entre 4 posições */}
|
||||
{activeView === 'transform' && step === 'choose' && (
|
||||
<div className="transform-view-wrapper">
|
||||
<p style={{ textAlign: 'center', color: 'var(--text-muted)', fontSize: '0.85rem', margin: '4px 0 16px' }}>
|
||||
Escolha a melhor orientação. Depois você faz o ajuste fino.
|
||||
</p>
|
||||
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: 12 }}>
|
||||
{[0, 90, 180, 270].map((deg) => (
|
||||
<button
|
||||
key={deg}
|
||||
type="button"
|
||||
onClick={() => { setBaseRotation(deg); setFineTuneAngle(0); setStep('finetune'); }}
|
||||
style={{
|
||||
border: '2px solid var(--glass-border)', borderRadius: 10, background: '#0b1220',
|
||||
padding: 8, cursor: 'pointer', display: 'flex', flexDirection: 'column',
|
||||
alignItems: 'center', gap: 6, transition: 'border-color .2s'
|
||||
}}
|
||||
onMouseEnter={(e) => (e.currentTarget.style.borderColor = 'var(--primary-color)')}
|
||||
onMouseLeave={(e) => (e.currentTarget.style.borderColor = 'var(--glass-border)')}
|
||||
title={`Usar ${deg}°`}
|
||||
>
|
||||
<div style={{ width: '100%', aspectRatio: '1 / 1', display: 'flex', alignItems: 'center', justifyContent: 'center', overflow: 'hidden' }}>
|
||||
<img
|
||||
src={imageUrl}
|
||||
alt={`${deg}°`}
|
||||
loading="lazy"
|
||||
style={{ maxWidth: '100%', maxHeight: '100%', objectFit: 'contain', transform: `rotate(${deg}deg)` }}
|
||||
/>
|
||||
</div>
|
||||
<span style={{ fontSize: '0.8rem', fontWeight: 600, color: 'var(--text-secondary)' }}>{deg}°</span>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* View: Transformação — PASSO 2: ajuste fino (escolhida + original) */}
|
||||
{activeView === 'transform' && step === 'finetune' && (
|
||||
<div className="transform-view-wrapper">
|
||||
<div className="new-transform-layout">
|
||||
{/* Original */}
|
||||
@@ -192,20 +234,27 @@ export default function TransformModal({ isOpen, onClose, image, onSaved }) {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Preview (com rotação aplicada) */}
|
||||
{/* Preview (orientação escolhida + ajuste fino) */}
|
||||
<div className="transform-panel panel-preview">
|
||||
<h3 className="panel-title">Preview</h3>
|
||||
<h3 className="panel-title">Preview ({totalRotation}°)</h3>
|
||||
<div className="img-container">
|
||||
<img
|
||||
src={imageUrl}
|
||||
alt="Preview"
|
||||
style={{ transform: `rotate(${fineTuneAngle}deg)` }}
|
||||
style={{ transform: `rotate(${baseRotation + fineTuneAngle}deg)` }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Controles de rotação */}
|
||||
<div className="transform-sidebar">
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-secondary btn-small"
|
||||
onClick={() => setStep('choose')}
|
||||
style={{ width: '100%', marginBottom: 12 }}
|
||||
title="Voltar para escolher a orientação"
|
||||
>← Posições</button>
|
||||
<h3 className="sidebar-title" style={{ fontSize: '0.65rem', textTransform: 'uppercase', letterSpacing: 1, color: 'var(--text-muted)', marginBottom: 12 }}>Ajuste</h3>
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 8, alignItems: 'center', width: '100%' }}>
|
||||
{[
|
||||
|
||||
Reference in New Issue
Block a user