fix(clipboard): corrigir erro de permissao de escrita e adicionar botao copiar nome
continuous-integration/webhook Deploy concluído (VPS4)
continuous-integration/webhook Deploy concluído (VPS4)
This commit is contained in:
@@ -59,6 +59,14 @@ function GtoDetailModal({ isOpen, onClose, gto, onUpdated }) {
|
|||||||
}
|
}
|
||||||
}, [selectedImage]);
|
}, [selectedImage]);
|
||||||
|
|
||||||
|
const getExtension = (filename) => {
|
||||||
|
if (!filename) return 'jpg';
|
||||||
|
const parts = filename.split('.');
|
||||||
|
if (parts.length < 2) return 'jpg';
|
||||||
|
const ext = parts[parts.length - 1].toLowerCase();
|
||||||
|
return ext === 'jpeg' ? 'jpg' : ext;
|
||||||
|
};
|
||||||
|
|
||||||
const handleDownload = async (img) => {
|
const handleDownload = async (img) => {
|
||||||
try {
|
try {
|
||||||
const url = img.filename?.startsWith('http')
|
const url = img.filename?.startsWith('http')
|
||||||
@@ -67,10 +75,11 @@ function GtoDetailModal({ isOpen, onClose, gto, onUpdated }) {
|
|||||||
const response = await fetch(url);
|
const response = await fetch(url);
|
||||||
const blob = await response.blob();
|
const blob = await response.blob();
|
||||||
|
|
||||||
|
const ext = getExtension(img.filename);
|
||||||
const pName = (img.patient_name || 'paciente').trim().replace(/[\/\\:*?"<>|]/g, '').replace(/\s+/g, '_');
|
const pName = (img.patient_name || 'paciente').trim().replace(/[\/\\:*?"<>|]/g, '').replace(/\s+/g, '_');
|
||||||
const obs = (img.remark || '').trim().replace(/[\/\\:*?"<>|]/g, '').replace(/\s+/g, '_');
|
const obs = (img.remark || '').trim().replace(/[\/\\:*?"<>|]/g, '').replace(/\s+/g, '_');
|
||||||
|
|
||||||
const fileName = obs ? `${pName}-${obs}.jpg` : `${pName}.jpg`;
|
const fileName = obs ? `${pName}-${obs}.${ext}` : `${pName}.${ext}`;
|
||||||
|
|
||||||
const a = document.createElement('a');
|
const a = document.createElement('a');
|
||||||
a.href = URL.createObjectURL(blob);
|
a.href = URL.createObjectURL(blob);
|
||||||
@@ -84,37 +93,61 @@ function GtoDetailModal({ isOpen, onClose, gto, onUpdated }) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleCopy = (img) => {
|
const handleCopyImage = async (img) => {
|
||||||
const url = img.filename?.startsWith('http')
|
const url = img.filename?.startsWith('http')
|
||||||
? img.filename
|
? img.filename
|
||||||
: `/uploads/${img.filename}`;
|
: `/uploads/${img.filename}`;
|
||||||
const imageObj = new Image();
|
|
||||||
imageObj.crossOrigin = 'anonymous';
|
try {
|
||||||
imageObj.onload = () => {
|
showToast('Processando cópia da imagem...', 'info');
|
||||||
try {
|
await navigator.clipboard.write([
|
||||||
const canvas = document.createElement('canvas');
|
new ClipboardItem({
|
||||||
canvas.width = imageObj.naturalWidth;
|
'image/png': new Promise((resolve, reject) => {
|
||||||
canvas.height = imageObj.naturalHeight;
|
const imageObj = new Image();
|
||||||
const ctx = canvas.getContext('2d');
|
imageObj.crossOrigin = 'anonymous';
|
||||||
ctx.drawImage(imageObj, 0, 0);
|
imageObj.onload = () => {
|
||||||
canvas.toBlob(async (blob) => {
|
try {
|
||||||
try {
|
const canvas = document.createElement('canvas');
|
||||||
await navigator.clipboard.write([
|
canvas.width = imageObj.naturalWidth;
|
||||||
new ClipboardItem({ 'image/png': blob })
|
canvas.height = imageObj.naturalHeight;
|
||||||
]);
|
const ctx = canvas.getContext('2d');
|
||||||
showToast('Imagem copiada para a área de transferência!', 'success');
|
ctx.drawImage(imageObj, 0, 0);
|
||||||
} catch (err) {
|
canvas.toBlob((blob) => {
|
||||||
showToast('Erro ao copiar imagem para área de transferência.', 'error');
|
if (blob) {
|
||||||
console.error(err);
|
resolve(blob);
|
||||||
}
|
} else {
|
||||||
}, 'image/png');
|
reject(new Error('Erro ao converter imagem em blob'));
|
||||||
} catch (e) {
|
}
|
||||||
showToast('Erro ao processar imagem para cópia.', 'error');
|
}, 'image/png');
|
||||||
console.error(e);
|
} catch (e) {
|
||||||
}
|
reject(e);
|
||||||
};
|
}
|
||||||
imageObj.onerror = () => showToast('Erro ao carregar imagem para cópia.', 'error');
|
};
|
||||||
imageObj.src = url;
|
imageObj.onerror = () => reject(new Error('Erro ao carregar a imagem'));
|
||||||
|
imageObj.src = url;
|
||||||
|
})
|
||||||
|
})
|
||||||
|
]);
|
||||||
|
showToast('Imagem copiada para a área de transferência!', 'success');
|
||||||
|
} catch (err) {
|
||||||
|
showToast('Erro ao copiar imagem. Certifique-se de que a página está focada.', 'error');
|
||||||
|
console.error(err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCopyFilename = async (img) => {
|
||||||
|
try {
|
||||||
|
const ext = getExtension(img.filename);
|
||||||
|
const pName = (img.patient_name || 'paciente').trim().replace(/[\/\\:*?"<>|]/g, '').replace(/\s+/g, '_');
|
||||||
|
const obs = (img.remark || '').trim().replace(/[\/\\:*?"<>|]/g, '').replace(/\s+/g, '_');
|
||||||
|
|
||||||
|
const fileName = obs ? `${pName}-${obs}.${ext}` : `${pName}.${ext}`;
|
||||||
|
|
||||||
|
await navigator.clipboard.writeText(fileName);
|
||||||
|
showToast('Nome do arquivo copiado para a área de transferência!', 'success');
|
||||||
|
} catch (err) {
|
||||||
|
showToast('Erro ao copiar nome do arquivo.', 'error');
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const saveImageRemark = async () => {
|
const saveImageRemark = async () => {
|
||||||
@@ -329,22 +362,30 @@ function GtoDetailModal({ isOpen, onClose, gto, onUpdated }) {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div style={{ display: 'flex', gap: 12, width: '100%' }}>
|
<div style={{ display: 'flex', gap: 12, width: '100%', flexWrap: 'wrap' }}>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="btn btn-primary"
|
className="btn btn-primary"
|
||||||
onClick={() => handleDownload(selectedImage)}
|
onClick={() => handleDownload(selectedImage)}
|
||||||
style={{ flex: 1, height: 46, fontSize: '0.95rem', fontWeight: 600, display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 8 }}
|
style={{ flex: 1, minWidth: 140, height: 46, fontSize: '0.95rem', fontWeight: 600, display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 8 }}
|
||||||
>
|
>
|
||||||
💾 Baixar
|
💾 Baixar
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="btn btn-secondary"
|
className="btn btn-secondary"
|
||||||
onClick={() => handleCopy(selectedImage)}
|
onClick={() => handleCopyImage(selectedImage)}
|
||||||
style={{ flex: 1, height: 46, fontSize: '0.95rem', fontWeight: 600, display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 8 }}
|
style={{ flex: 1, minWidth: 140, height: 46, fontSize: '0.95rem', fontWeight: 600, display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 8 }}
|
||||||
>
|
>
|
||||||
📋 Copiar para Área de Transferência
|
🖼️ Copiar Imagem
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="btn btn-secondary"
|
||||||
|
onClick={() => handleCopyFilename(selectedImage)}
|
||||||
|
style={{ flex: 1, minWidth: 140, height: 46, fontSize: '0.95rem', fontWeight: 600, display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 8 }}
|
||||||
|
>
|
||||||
|
📋 Copiar Nome
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -246,47 +246,54 @@ export default function ImageDetailsPage() {
|
|||||||
img.src = url;
|
img.src = url;
|
||||||
};
|
};
|
||||||
|
|
||||||
const copyImageToClipboard = () => {
|
const copyImageToClipboard = async () => {
|
||||||
const url = imageUrl;
|
const url = imageUrl;
|
||||||
const img = new Image();
|
|
||||||
img.crossOrigin = 'anonymous';
|
try {
|
||||||
img.onload = () => {
|
showToast('Processando cópia da imagem...', 'info');
|
||||||
try {
|
await navigator.clipboard.write([
|
||||||
const w = img.naturalWidth, h = img.naturalHeight;
|
new ClipboardItem({
|
||||||
const deg = totalRotation;
|
'image/png': new Promise((resolve, reject) => {
|
||||||
const fH = baseFlipH;
|
const img = new Image();
|
||||||
const fV = baseFlipV;
|
img.crossOrigin = 'anonymous';
|
||||||
const rad = (deg * Math.PI) / 180;
|
img.onload = () => {
|
||||||
const cos = Math.abs(Math.cos(rad)), sin = Math.abs(Math.sin(rad));
|
try {
|
||||||
const cw = Math.round(w * cos + h * sin);
|
const w = img.naturalWidth, h = img.naturalHeight;
|
||||||
const ch = Math.round(w * sin + h * cos);
|
const deg = totalRotation;
|
||||||
const canvas = document.createElement('canvas');
|
const fH = baseFlipH;
|
||||||
canvas.width = cw; canvas.height = ch;
|
const fV = baseFlipV;
|
||||||
const ctx = canvas.getContext('2d');
|
const rad = (deg * Math.PI) / 180;
|
||||||
ctx.translate(cw / 2, ch / 2);
|
const cos = Math.abs(Math.cos(rad)), sin = Math.abs(Math.sin(rad));
|
||||||
ctx.scale(fH ? -1 : 1, fV ? -1 : 1);
|
const cw = Math.round(w * cos + h * sin);
|
||||||
ctx.rotate(rad);
|
const ch = Math.round(w * sin + h * cos);
|
||||||
ctx.drawImage(img, -w / 2, -h / 2);
|
const canvas = document.createElement('canvas');
|
||||||
canvas.toBlob(async (blob) => {
|
canvas.width = cw; canvas.height = ch;
|
||||||
try {
|
const ctx = canvas.getContext('2d');
|
||||||
await navigator.clipboard.write([
|
ctx.translate(cw / 2, ch / 2);
|
||||||
new ClipboardItem({
|
ctx.scale(fH ? -1 : 1, fV ? -1 : 1);
|
||||||
'image/png': blob
|
ctx.rotate(rad);
|
||||||
})
|
ctx.drawImage(img, -w / 2, -h / 2);
|
||||||
]);
|
canvas.toBlob((blob) => {
|
||||||
showToast('Imagem copiada para a área de transferência!', 'success');
|
if (blob) {
|
||||||
} catch (err) {
|
resolve(blob);
|
||||||
showToast('Erro ao copiar imagem para clipboard.', 'error');
|
} else {
|
||||||
console.error(err);
|
reject(new Error('Erro ao converter canvas em blob'));
|
||||||
}
|
}
|
||||||
}, 'image/png');
|
}, 'image/png');
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
showToast('Erro ao processar imagem para cópia.', 'error');
|
reject(e);
|
||||||
console.error(e);
|
}
|
||||||
}
|
};
|
||||||
};
|
img.onerror = () => reject(new Error('Erro ao carregar imagem para cópia.'));
|
||||||
img.onerror = () => showToast('Erro ao carregar imagem para cópia.', 'error');
|
img.src = url;
|
||||||
img.src = url;
|
})
|
||||||
|
})
|
||||||
|
]);
|
||||||
|
showToast('Imagem copiada para a área de transferência!', 'success');
|
||||||
|
} catch (err) {
|
||||||
|
showToast('Erro ao copiar imagem. Certifique-se de que a página está focada.', 'error');
|
||||||
|
console.error(err);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const markGtoSent = async (gtoId, sent) => {
|
const markGtoSent = async (gtoId, sent) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user