feat(observation): separar observacao de imagem da observacao global do paciente (image_remark)
continuous-integration/webhook Deploy concluído (VPS4)

This commit is contained in:
VPS 4 Deploy Agent
2026-06-01 03:48:34 +02:00
parent 241a194f2a
commit d86def2f12
4 changed files with 28 additions and 24 deletions
+1
View File
@@ -94,6 +94,7 @@ async function createTables() {
// Garantir que a coluna exista caso a tabela já tenha sido criada antes
await client.query('ALTER TABLE images ADD COLUMN IF NOT EXISTS thumb_filename TEXT;');
await client.query('ALTER TABLE images ADD COLUMN IF NOT EXISTS clinic_name VARCHAR(100);');
await client.query('ALTER TABLE images ADD COLUMN IF NOT EXISTS image_remark TEXT;');
console.log('✅ Tabela images criada/verificada no PostgreSQL');
@@ -54,7 +54,7 @@ function GtoDetailModal({ isOpen, onClose, gto, onUpdated }) {
useEffect(() => {
if (selectedImage) {
setTempRemark(selectedImage.remark || '');
setTempRemark(selectedImage.image_remark || '');
setIsEditingRemark(false);
}
}, [selectedImage]);
@@ -77,7 +77,7 @@ function GtoDetailModal({ isOpen, onClose, gto, onUpdated }) {
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 obs = (img.image_remark || '').trim().replace(/[\/\\:*?"<>|]/g, '').replace(/\s+/g, '_');
const fileName = obs ? `${pName}-${obs}.${ext}` : `${pName}.${ext}`;
@@ -139,7 +139,7 @@ function GtoDetailModal({ isOpen, onClose, gto, onUpdated }) {
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 obs = (img.image_remark || '').trim().replace(/[\/\\:*?"<>|]/g, '').replace(/\s+/g, '_');
const fileName = obs ? `${pName}-${obs}.${ext}` : `${pName}.${ext}`;
@@ -153,9 +153,9 @@ function GtoDetailModal({ isOpen, onClose, gto, onUpdated }) {
const saveImageRemark = async () => {
setSavingRemark(true);
try {
await api.put(`/images/${selectedImage.id}/remark`, { remark: tempRemark });
await api.put(`/images/${selectedImage.id}/remark`, { image_remark: tempRemark });
showToast('Observação atualizada com sucesso!', 'success');
setSelectedImage(prev => ({ ...prev, remark: tempRemark }));
setSelectedImage(prev => ({ ...prev, image_remark: tempRemark }));
setIsEditingRemark(false);
loadDetail();
onUpdated?.();
@@ -344,14 +344,14 @@ function GtoDetailModal({ isOpen, onClose, gto, onUpdated }) {
</div>
) : (
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', gap: 12 }}>
<div style={{ fontSize: '0.95rem', color: selectedImage.remark ? 'var(--text-primary)' : 'var(--text-muted)', fontStyle: selectedImage.remark ? 'normal' : 'italic' }}>
{selectedImage.remark || 'Nenhuma observação adicionada.'}
<div style={{ fontSize: '0.95rem', color: selectedImage.image_remark ? 'var(--text-primary)' : 'var(--text-muted)', fontStyle: selectedImage.image_remark ? 'normal' : 'italic' }}>
{selectedImage.image_remark || 'Nenhuma observação adicionada.'}
</div>
<button
type="button"
className="btn btn-secondary btn-small"
onClick={() => {
setTempRemark(selectedImage.remark || '');
setTempRemark(selectedImage.image_remark || '');
setIsEditingRemark(true);
}}
style={{ padding: '4px 10px', fontSize: '0.8rem', flexShrink: 0 }}
@@ -82,11 +82,11 @@ export default function ImageDetailsPage() {
setFineTuneAngle(0);
setBaseRotation(0);
setBaseFlipH(false);
setBaseFlipV(false);
setBaseFlipV(image.flip_vertical === 1);
setChoosePage(0);
setOrientationSubpage(0);
setStep('choose');
setRemark(image.remark || ''); // carrega remark existente se houver
setRemark(image.image_remark || ''); // carrega remark existente se houver
setShowInfo(false);
setActiveView('transform');
@@ -153,7 +153,7 @@ export default function ImageDetailsPage() {
const totalRotation = ((baseRotation + fineTuneAngle) % 360 + 360) % 360;
const saveTransform = async () => {
if (totalRotation === 0 && !baseFlipH && !baseFlipV && remark === (image.remark || '')) {
if (totalRotation === 0 && !baseFlipH && !baseFlipV && remark === (image.image_remark || '')) {
showToast('Nenhuma edição aplicada.', 'error');
return;
}
@@ -163,7 +163,7 @@ export default function ImageDetailsPage() {
rotation: totalRotation,
flipH: baseFlipH,
flipV: baseFlipV,
remark
image_remark: remark
});
showToast('Imagem e orientação salvas!', 'success');
// Atualiza a rota com o novo ID gerado em background (sem adicionar ao histórico de navegação)
@@ -180,7 +180,7 @@ export default function ImageDetailsPage() {
setResetting(true);
try {
await api.post(`/images/${image.id}/transform`, {
rotation: 0, flipH: false, flipV: false, remark: ''
rotation: 0, flipH: false, flipV: false, image_remark: ''
});
showToast('Imagem restaurada para o original!', 'success');
navigate('/' + encodeURIComponent(patientName));
@@ -233,8 +233,10 @@ export default function ImageDetailsPage() {
canvas.toBlob((blob) => {
const a = document.createElement('a');
a.href = URL.createObjectURL(blob);
const safe = (image.patient_name || 'rx').replace(/[^\w\-]+/g, '_');
a.download = `${safe}_${applyTransform ? deg + 'deg' : 'original'}.png`;
const pName = (image.patient_name || 'paciente').trim().replace(/[\/\\:*?"<>|]/g, '').replace(/\s+/g, '_');
const obs = (image.image_remark || '').trim().replace(/[\/\\:*?"<>|]/g, '').replace(/\s+/g, '_');
const suffix = applyTransform ? `${deg}deg` : 'original';
a.download = obs ? `${pName}-${obs}_${suffix}.png` : `${pName}_${suffix}.png`;
document.body.appendChild(a); a.click(); a.remove();
setTimeout(() => URL.revokeObjectURL(a.href), 4000);
}, 'image/png');
@@ -391,7 +393,7 @@ export default function ImageDetailsPage() {
{showInfo && (
<div className="extra-info-panel" style={{ marginBottom: 20 }}>
<div style={{ marginBottom: 5 }}><strong>👨 Dentista:</strong> {image.doctor || 'Não informado'}</div>
<div><strong>📝 Obs:</strong> {image.remark || 'Nenhuma observação'}</div>
<div><strong>📝 Obs:</strong> {image.image_remark || 'Nenhuma observação'}</div>
</div>
)}
+9 -8
View File
@@ -366,7 +366,7 @@ router.get('/:id/transformations', async (req, res) => {
// ================================================================
router.post('/:id/transform', async (req, res) => {
try {
const { rotation, flipH, flipV, remark } = req.body;
const { rotation, flipH, flipV, image_remark } = req.body;
const originalImage = await db.get('SELECT * FROM images WHERE id = $1', [req.params.id]);
if (!originalImage) {
@@ -396,14 +396,14 @@ router.post('/:id/transform', async (req, res) => {
// Desabilitar a imagem active atual para que não fique duplicada na interface
await db.run('UPDATE images SET enabled = 0 WHERE id = $1', [req.params.id]);
const newRemark = remark !== undefined ? remark : originalImage.remark;
const newImageRemark = image_remark !== undefined ? image_remark : originalImage.image_remark;
// Inserir a nova imagem como a versão ativa
const result = await db.run(
`INSERT INTO images (
image_guid, patient_name, clinic_name, client_name, original_filename, filename,
enabled, rotation, flip_horizontal, flip_vertical, original_image_id, created_at, remark, doctor
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14) RETURNING id`,
enabled, rotation, flip_horizontal, flip_vertical, original_image_id, created_at, remark, image_remark, doctor
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15) RETURNING id`,
[
`${realOriginal.image_guid}_${timestamp}`,
realOriginal.patient_name,
@@ -417,7 +417,8 @@ router.post('/:id/transform', async (req, res) => {
flipV ? 1 : 0,
realOriginalId,
originalImage.created_at, // Manter a data de criação original
newRemark,
originalImage.remark, // Manter a observação do paciente intacta
newImageRemark, // A observação específica da imagem
realOriginal.doctor
]
);
@@ -438,10 +439,10 @@ router.post('/:id/transform', async (req, res) => {
// ================================================================
router.put('/:id/remark', async (req, res) => {
try {
const { remark } = req.body;
const { image_remark } = req.body;
await db.run(
'UPDATE images SET remark = $1 WHERE id = $2',
[remark, req.params.id]
'UPDATE images SET image_remark = $1 WHERE id = $2',
[image_remark, req.params.id]
);
res.json({ success: true, message: 'Observação da imagem atualizada!' });
} catch (error) {