feat(observation): separar observacao de imagem da observacao global do paciente (image_remark)
continuous-integration/webhook Deploy concluído (VPS4)
continuous-integration/webhook Deploy concluído (VPS4)
This commit is contained in:
@@ -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>
|
||||
)}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user