fix(clipboard): corrigir erro de permissao de escrita e adicionar botao copiar nome
continuous-integration/webhook Deploy concluído (VPS4)

This commit is contained in:
VPS 4 Deploy Agent
2026-06-01 03:18:36 +02:00
parent e929f108ff
commit c3704fdc27
2 changed files with 122 additions and 74 deletions
@@ -59,6 +59,14 @@ function GtoDetailModal({ isOpen, onClose, gto, onUpdated }) {
}
}, [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) => {
try {
const url = img.filename?.startsWith('http')
@@ -67,10 +75,11 @@ function GtoDetailModal({ isOpen, onClose, gto, onUpdated }) {
const response = await fetch(url);
const blob = await response.blob();
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}.jpg` : `${pName}.jpg`;
const fileName = obs ? `${pName}-${obs}.${ext}` : `${pName}.${ext}`;
const a = document.createElement('a');
a.href = URL.createObjectURL(blob);
@@ -84,10 +93,16 @@ function GtoDetailModal({ isOpen, onClose, gto, onUpdated }) {
}
};
const handleCopy = (img) => {
const handleCopyImage = async (img) => {
const url = img.filename?.startsWith('http')
? img.filename
: `/uploads/${img.filename}`;
try {
showToast('Processando cópia da imagem...', 'info');
await navigator.clipboard.write([
new ClipboardItem({
'image/png': new Promise((resolve, reject) => {
const imageObj = new Image();
imageObj.crossOrigin = 'anonymous';
imageObj.onload = () => {
@@ -97,24 +112,42 @@ function GtoDetailModal({ isOpen, onClose, gto, onUpdated }) {
canvas.height = imageObj.naturalHeight;
const ctx = canvas.getContext('2d');
ctx.drawImage(imageObj, 0, 0);
canvas.toBlob(async (blob) => {
try {
await navigator.clipboard.write([
new ClipboardItem({ 'image/png': blob })
]);
showToast('Imagem copiada para a área de transferência!', 'success');
} catch (err) {
showToast('Erro ao copiar imagem para área de transferência.', 'error');
console.error(err);
canvas.toBlob((blob) => {
if (blob) {
resolve(blob);
} else {
reject(new Error('Erro ao converter imagem em blob'));
}
}, 'image/png');
} catch (e) {
showToast('Erro ao processar imagem para cópia.', 'error');
console.error(e);
reject(e);
}
};
imageObj.onerror = () => showToast('Erro ao carregar imagem para cópia.', 'error');
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 () => {
@@ -329,22 +362,30 @@ function GtoDetailModal({ isOpen, onClose, gto, onUpdated }) {
)}
</div>
<div style={{ display: 'flex', gap: 12, width: '100%' }}>
<div style={{ display: 'flex', gap: 12, width: '100%', flexWrap: 'wrap' }}>
<button
type="button"
className="btn btn-primary"
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
</button>
<button
type="button"
className="btn btn-secondary"
onClick={() => handleCopy(selectedImage)}
style={{ flex: 1, height: 46, fontSize: '0.95rem', fontWeight: 600, display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 8 }}
onClick={() => handleCopyImage(selectedImage)}
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>
</div>
</div>
@@ -246,8 +246,14 @@ export default function ImageDetailsPage() {
img.src = url;
};
const copyImageToClipboard = () => {
const copyImageToClipboard = async () => {
const url = imageUrl;
try {
showToast('Processando cópia da imagem...', 'info');
await navigator.clipboard.write([
new ClipboardItem({
'image/png': new Promise((resolve, reject) => {
const img = new Image();
img.crossOrigin = 'anonymous';
img.onload = () => {
@@ -267,26 +273,27 @@ export default function ImageDetailsPage() {
ctx.scale(fH ? -1 : 1, fV ? -1 : 1);
ctx.rotate(rad);
ctx.drawImage(img, -w / 2, -h / 2);
canvas.toBlob(async (blob) => {
try {
await navigator.clipboard.write([
new ClipboardItem({
'image/png': blob
canvas.toBlob((blob) => {
if (blob) {
resolve(blob);
} else {
reject(new Error('Erro ao converter canvas em blob'));
}
}, 'image/png');
} catch (e) {
reject(e);
}
};
img.onerror = () => reject(new Error('Erro ao carregar imagem para cópia.'));
img.src = url;
})
})
]);
showToast('Imagem copiada para a área de transferência!', 'success');
} catch (err) {
showToast('Erro ao copiar imagem para clipboard.', 'error');
showToast('Erro ao copiar imagem. Certifique-se de que a página está focada.', 'error');
console.error(err);
}
}, 'image/png');
} catch (e) {
showToast('Erro ao processar imagem para cópia.', 'error');
console.error(e);
}
};
img.onerror = () => showToast('Erro ao carregar imagem para cópia.', 'error');
img.src = url;
};
const markGtoSent = async (gtoId, sent) => {