Skip to main content

Notes System Overview

The Dental Odontogram application provides a comprehensive notes system that allows dental professionals to add detailed clinical observations for each tooth with treatments.

Per-Tooth Notes

Notes are automatically created for each tooth that has treatments applied. They appear in the notes section below the odontogram canvas.

Global Notes Storage

// Global variable for storing notes
let toothNotes = {}
Notes are stored in memory using tooth FDI numbers as keys:
{
  '16': 'Large cavity on mesial surface, needs immediate attention',
  '26': 'Crown scheduled for next month',
  '36': 'Patient reports sensitivity'
}

Adding Notes

Automatic Note Fields

When you apply a treatment to a tooth, a note field is automatically generated:
// ADD NOTES SECTION - from updateOdontogramData function
const existingNote = loadToothNote(toothNum)
html += `
  <div class="tooth-notes-section">
    <div class="notes-header">
      <label for="notes-${toothNum}">Notas:</label>
      <div class="notes-controls">
        <button type="button" class="save-note-btn" 
                data-tooth="${toothNum}" title="Guardar nota">
          💾
        </button>
        <button type="button" class="clear-note-btn" 
                data-tooth="${toothNum}" title="Limpiar nota">
          🗑️
        </button>
      </div>
    </div>
    <textarea 
      id="notes-${toothNum}" 
      class="tooth-notes" 
      placeholder="Agregar notas clínicas para diente ${toothNum}..."
      rows="3"
      data-tooth="${toothNum}">${existingNote}</textarea>
    <div class="note-status" id="status-${toothNum}"></div>
  </div>
`

Note Field Components

Label

Shows “Notas:” and tooth number in placeholder

Textarea

3-row expandable text field for clinical notes

Controls

Save and clear buttons for manual control

Auto-Save Feature

Notes automatically save after 2 seconds of inactivity:
// Enhanced notes handling with save functionality
$(document).on('input', '.tooth-notes', function() {
  const toothNum = $(this).data('tooth')
  const noteText = $(this).val()
  
  // Auto-save after 2 seconds of no typing
  clearTimeout($(this).data('saveTimeout'))
  const saveTimeout = setTimeout(() => {
    saveToothNote(toothNum, noteText)
    $(`#status-${toothNum}`)
      .text('✅ Guardado automáticamente')
      .addClass('auto-saved')
    setTimeout(() => {
      $(`#status-${toothNum}`).removeClass('auto-saved').text('')
    }, 2000)
  }, 2000)
  
  $(this).data('saveTimeout', saveTimeout)
  $(`#status-${toothNum}`).text('✏️ Editando...').removeClass('auto-saved')
})

Auto-Save Workflow

1

User types in note field

Input event is triggered.
2

Status shows 'Editando...'

Visual feedback that text is being edited.
3

2-second timer starts

Each keystroke resets the timer.
4

Timer completes

Note is automatically saved after 2 seconds of no typing.
5

Status shows 'Guardado automáticamente'

Confirmation message appears for 2 seconds, then disappears.

Manual Save

Users can manually save notes at any time:
// Manual save button for notes
$(document).on('click', '.save-note-btn', function() {
  const toothNum = $(this).data('tooth')
  const noteText = $(`#notes-${toothNum}`).val()
  
  saveToothNote(toothNum, noteText)
  $(`#status-${toothNum}`)
    .text('💾 Nota guardada manualmente')
    .addClass('manual-saved')
  
  setTimeout(() => {
    $(`#status-${toothNum}`).removeClass('manual-saved').text('')
  }, 3000)
})

Manual Save Benefits

  • Immediate save without waiting for auto-save timer
  • Visual confirmation with ”💾 Nota guardada manualmente” message
  • Longer feedback (3 seconds vs 2 seconds for auto-save)

Save Function

The core save function:
/**
 * Save note for specific tooth
 */
function saveToothNote(toothNum, noteText) {
  toothNotes[toothNum] = noteText
  
  // Visual feedback for save
  const noteElement = $(`#notes-${toothNum}`)
  noteElement.addClass('note-saved')
  
  setTimeout(() => {
    noteElement.removeClass('note-saved')
  }, 1000)
  
  console.log(`📝 Note saved for tooth ${toothNum}:`, noteText)
}

Load Function

Retrieve saved notes:
/**
 * Load note for specific tooth
 */
function loadToothNote(toothNum) {
  return toothNotes[toothNum] || ''
}
Notes are loaded when:
  • Treatment data is updated
  • Odontogram is refreshed
  • User switches between teeth

Clear Notes

Notes can be cleared individually:
// Clear note button
$(document).on('click', '.clear-note-btn', function() {
  const toothNum = $(this).data('tooth')
  
  if (confirm(`¿Está seguro que desea eliminar la nota del diente ${toothNum}?`)) {
    $(`#notes-${toothNum}`).val('')
    delete toothNotes[toothNum]
    $(`#status-${toothNum}`)
      .text('🗑️ Nota eliminada')
      .addClass('note-deleted')
    
    setTimeout(() => {
      $(`#status-${toothNum}`).removeClass('note-deleted').text('')
    }, 2000)
    
    console.log(`🗑️ Note cleared for tooth ${toothNum}`)
  }
})
Clearing a note requires confirmation and permanently removes the text. This action cannot be undone.

Notes Display

Notes appear in the treatment summary for each tooth:
function updateOdontogramData(geometry) {
  // ... treatment rendering ...
  
  // Notes section for each tooth
  const existingNote = loadToothNote(toothNum)
  html += `
    <div class="tooth-notes-section">
      <div class="notes-header">
        <label for="notes-${toothNum}">Notas:</label>
        <div class="notes-controls">
          <button type="button" class="save-note-btn" data-tooth="${toothNum}">
            💾
          </button>
          <button type="button" class="clear-note-btn" data-tooth="${toothNum}">
            🗑️
          </button>
        </div>
      </div>
      <textarea id="notes-${toothNum}" class="tooth-notes" 
                data-tooth="${toothNum}">${existingNote}</textarea>
      <div class="note-status" id="status-${toothNum}"></div>
    </div>
  `
}

Notes in Data Export

Notes are included in JSON export:
function exportOdontogramData() {
  const exportData = {
    fecha: now.toISOString().split('T')[0],
    nombre: 'PACIENTE',
    piezas: []
  }
  
  // Process each tooth
  for (const [key, treatments] of Object.entries(currentGeometry)) {
    if (toothNum) {
      const toothData = {
        pieza: toothNum,
        condiciones: [],
        prestacion_requerida: [],
        prestacion_preexistente: [],
        notas: toothNotes[toothNum] || ''  // Include notes
      }
      
      // ... process treatments ...
      
      // Only export if has treatments OR notes
      if (
        toothData.condiciones.length > 0 ||
        toothData.prestacion_requerida.length > 0 ||
        toothData.prestacion_preexistente.length > 0 ||
        toothData.notas.trim() !== ''
      ) {
        exportData.piezas.push(toothData)
      }
    }
  }
}
{
  "pieza": "16",
  "condiciones": [],
  "prestacion_preexistente": [
    "Obturación - Cara/s: Oclusal"
  ],
  "prestacion_requerida": [
    "Corona"
  ],
  "notas": "Large filling from 2020 showing wear. Patient reports no pain. Crown recommended to prevent fracture. Schedule for next month."
}

Notes in PNG Report

Notes are rendered in the professional PNG report:
function generateProfessionalPNG() {
  // ... render odontogram ...
  
  // For each tooth with treatments
  function renderToothData(tooth, x, y) {
    // ... render treatments ...
    
    // NOTES - MUCH BIGGER FONTS
    if (tooth.note) {
      ctx.fillStyle = '#34495e'
      ctx.font = 'bold 28px Arial'  // Large label
      ctx.fillText('Notas:', x + 20, localY)
      localY += 35
      
      // Word wrap notes with bigger font
      ctx.fillStyle = '#34495e'
      ctx.font = '24px Arial'  // Large text
      const words = tooth.note.split(' ')
      let line = ''
      const maxWidth = columnWidth - 60
      const lineHeight = 30
      
      for (const word of words) {
        const testLine = line + word + ' '
        const metrics = ctx.measureText(testLine)
        
        if (metrics.width > maxWidth && line !== '') {
          ctx.fillText(line.trim(), x + 40, localY)
          line = word + ' '
          localY += lineHeight
        } else {
          line = testLine
        }
      }
      
      if (line.trim()) {
        ctx.fillText(line.trim(), x + 40, localY)
        localY += lineHeight + 20
      }
    }
  }
}
Notes appear in the PNG report with:
  • 28px bold “Notas:” label
  • 24px note text
  • Word wrapping for long notes
  • Prominent placement after treatment lists

Status Indicators

Visual Feedback States

Message: “✏️ Editando…”When: User is typing in note fieldDuration: Visible while typing, disappears when auto-save triggers
Message: ”✅ Guardado automáticamente”When: Auto-save completes (2 seconds after last keystroke)Duration: 2 secondsCSS Class: .auto-saved
Message: ”💾 Nota guardada manualmente”When: User clicks save buttonDuration: 3 secondsCSS Class: .manual-saved
Message: “🗑️ Nota eliminada”When: User confirms note deletionDuration: 2 secondsCSS Class: .note-deleted

Best Practices

Be Specific

Include details like dates, symptoms, patient feedback, and treatment rationale.

Use Clinical Terms

Maintain professional terminology for clear communication.

Note Timing

Document when treatments were done or when they’re scheduled.

Patient Concerns

Record any patient-reported symptoms or concerns.

Example Notes

Large amalgam filling placed in 2019. 
Patient reports no sensitivity. 
Filling appears intact with good marginal seal.
Monitor at next cleaning.
Longer, detailed notes in the odontogram provide better context for treatment planning and communication with colleagues or specialists.

Build docs developers (and LLMs) love