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]); }, [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) => {