# View Patterns - Padrões de Visualização PharmData

> Padrões para páginas read-only de visualização estruturada de dados

---

## Filosofia

Páginas de visualização (views) servem para **apresentar informações de forma clara e navegável**, enquanto formulários servem para **editar dados**. A separação clara entre esses dois contextos melhora a experiência do usuário.

---

## Princípios de View Pages

### 1. Read-Only First
- Dados apresentados em formato não-editável
- Foco na legibilidade e compreensão
- Links externos para bases de dados relacionadas
- Botão "Editar" para transição ao formulário

### 2. Estruturação Visual
- **Property Grids**: Pares label-value organizados em grid 2 colunas
- **Cards Temáticos**: Agrupamento por contexto (identificação, propriedades, classificações)
- **Progressive Disclosure**: Informações adicionais recolhíveis

### 3. Visualizações Especializadas
- Gráficos e estruturas (moleculares, organogramas)
- Fórmulas formatadas (subscripts, superscripts)
- Taxonomias e hierarquias

---

## Anatomia de uma View Page

```
┌─────────────────────────────────────────────┐
│ Header (emerald-abyss)                      │
│ Logo │ Navigation                            │
└─────────────────────────────────────────────┘
┌─────────────────────────────────────────────┐
│ Breadcrumb + Actions                        │
│ Substâncias > SUB-082615    [Histórico] [Editar]│
└─────────────────────────────────────────────┘
┌─────────────────────────────────────────────┐
│ Page Header (white card)                    │
│ BK │ Status │ Quality                       │
│ Nome Principal                              │
│ Sinônimos                                   │
│ Fórmula Molecular: C₈H₉NO₂                  │
└─────────────────────────────────────────────┘
┌──────────────────┬──────────────────────────┐
│ Left Column      │ Right Column             │
│                  │                          │
│ [Estrutura 2D]   │ [Identificadores]        │
│                  │                          │
│ [Propriedades]   │ [Classificações]         │
│                  │                          │
│                  │ [Additional Info ▼]      │
└──────────────────┴──────────────────────────┘
```

---

## Padrões de Layout

### Layout de 2 Colunas

**Uso**: Quando há visualizações especializadas (estruturas, gráficos) + dados tabulares

```html
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
    <!-- Coluna Esquerda: Visualizações -->
    <div class="space-y-6">
        <div class="bg-white border rounded-lg p-6">
            <!-- Estrutura molecular, gráfico, etc. -->
        </div>
        <div class="bg-white border rounded-lg p-6">
            <!-- Propriedades físico-químicas -->
        </div>
    </div>

    <!-- Coluna Direita: Dados Estruturados -->
    <div class="space-y-6">
        <div class="bg-white border rounded-lg p-6">
            <!-- Identificadores -->
        </div>
        <div class="bg-white border rounded-lg p-6">
            <!-- Classificações -->
        </div>
    </div>
</div>
```

### Layout de Coluna Única

**Uso**: Quando não há visualizações especializadas, apenas dados tabulares

```html
<div class="max-w-4xl mx-auto space-y-6">
    <div class="bg-white border rounded-lg p-6">
        <!-- Seção 1 -->
    </div>
    <div class="bg-white border rounded-lg p-6">
        <!-- Seção 2 -->
    </div>
</div>
```

---

## Property Grid Pattern

Grid de 2 colunas para exibir atributos de forma organizada.

### Estrutura

```html
<div class="property-grid">
    <div class="property-label">Peso Molecular</div>
    <div class="property-value">151.163 g/mol</div>

    <div class="property-label">Estado Físico</div>
    <div class="property-value">Sólido cristalino branco</div>
</div>
```

### CSS

```css
.property-grid {
    display: grid;
    grid-template-columns: 160px 1fr;
    gap: 12px;
}

.property-label {
    font-weight: 600;
    color: var(--soft-steel);
    font-size: 0.875rem;
}

.property-value {
    color: var(--graphite-depth);
    font-size: 0.875rem;
}
```

### Aplicação com Modelo EAV

O property grid é ideal para renderizar atributos dinâmicos vindos de tabelas EAV como `SUBSTANCE_ATTRIBUTES`:

```python
# Flask view
@app.route('/substances/<business_key>')
def substance_view(business_key):
    substance = get_substance(business_key)
    attributes = get_substance_attributes(substance.id)

    return render_template('substance-view.html',
                         substance=substance,
                         attributes=attributes)
```

```jinja2
<!-- Template -->
<div class="property-grid">
    {% for attr in attributes %}
    <div class="property-label">{{ attr.label }}</div>
    <div class="property-value">
        {{ attr.value }}
        {% if attr.unit %} {{ attr.unit }}{% endif %}
    </div>
    {% endfor %}
</div>
```

---

## Visualizações Especializadas

### Estrutura Molecular 2D

**Biblioteca**: SmilesDrawer (open source, leve, sem dependências)

```html
<div id="structure-canvas">
    <canvas id="structure-render" width="400" height="300"></canvas>
</div>

<script src="https://unpkg.com/smiles-drawer@2.0.1/dist/smiles-drawer.min.js"></script>
<script>
    const smilesDrawer = new SmilesDrawer.Drawer({
        width: 400,
        height: 300,
        bondThickness: 2
    });

    const smiles = 'CC(=O)Nc1ccc(O)cc1'; // Paracetamol
    SmilesDrawer.parse(smiles, function(tree) {
        smilesDrawer.draw(tree, 'structure-render', 'light', false);
    });
</script>
```

**Alternativas**:
- **Kekule.js**: Mais recursos, maior bundle
- **ChemDoodle Web Components**: Comercial, robusto
- **RDKit.js**: Baseado em RDKit, WebAssembly

### Fórmula Molecular Formatada

Use subscripts e superscripts HTML:

```html
<p class="molecular-formula">
    C<sub>8</sub>H<sub>9</sub>NO<sub>2</sub>
</p>

<style>
.molecular-formula {
    font-family: 'Courier New', monospace;
    font-size: 1.5rem;
    font-weight: 500;
}
</style>
```

**Para íons e cargas**:

```html
SO<sub>4</sub><sup>2-</sup>
Ca<sup>2+</sup>
```

---

## Navegação Entre Contextos

### View → Edit

```html
<div class="flex items-center gap-2">
    <a href="/substances/{{ substance.business_key }}/edit"
       class="px-3 py-2 text-sm text-white bg-teal-intense hover:bg-emerald-abyss rounded-lg">
        <i class="fas fa-edit mr-2"></i>Editar
    </a>
</div>
```

### List → View

```html
<div class="table-row cursor-pointer"
     onclick="window.location='/substances/{{ substance.business_key }}'">
    <!-- Conteúdo da linha -->
</div>
```

### Breadcrumb

```html
<div class="flex items-center gap-2 text-sm text-soft-steel">
    <a href="/substances" class="hover:text-teal-intense">Substâncias</a>
    <i class="fas fa-chevron-right text-xs"></i>
    <span class="text-graphite-depth font-medium">{{ substance.business_key }}</span>
</div>
```

---

## Progressive Disclosure em Views

Informações menos frequentes podem ser recolhidas:

```html
<div class="bg-white border rounded-lg p-6">
    <button onclick="toggleAdditional()"
            class="w-full flex items-center justify-between text-left">
        <span class="section-header">
            <i class="fas fa-plus-circle mr-2"></i>Informações Adicionais
        </span>
        <i class="fas fa-chevron-down transition-transform" id="toggle-icon"></i>
    </button>

    <div id="additional-content" class="additional-section mt-4">
        <!-- Conteúdo adicional -->
    </div>
</div>

<style>
.additional-section {
    max-height: 0;
    overflow: hidden;
    transition: max-height 0.3s ease-out;
}
.additional-section.open {
    max-height: 2000px;
}
</style>

<script>
function toggleAdditional() {
    const content = document.getElementById('additional-content');
    const icon = document.getElementById('toggle-icon');

    if (content.classList.contains('open')) {
        content.classList.remove('open');
        icon.style.transform = 'rotate(0deg)';
    } else {
        content.classList.add('open');
        icon.style.transform = 'rotate(180deg)';
    }
}
</script>
```

---

## Links Externos

Para bases de dados externas:

```html
<div class="property-grid">
    <div class="property-label">PubChem CID</div>
    <div class="property-value">
        <a href="https://pubchem.ncbi.nlm.nih.gov/compound/{{ substance.pubchem_cid }}"
           target="_blank"
           class="text-teal-intense hover:underline">
            {{ substance.pubchem_cid }}
        </a>
    </div>

    <div class="property-label">ChEMBL ID</div>
    <div class="property-value">
        <a href="https://www.ebi.ac.uk/chembl/compound_report_card/{{ substance.chembl_id }}/"
           target="_blank"
           class="text-teal-intense hover:underline">
            {{ substance.chembl_id }}
        </a>
    </div>
</div>
```

---

## Exemplo Completo: Substance View

Ver implementação completa em [examples/substance-view.html](../examples/substance-view.html)

**Recursos implementados**:
- Layout 2 colunas (estrutura + dados)
- Estrutura molecular 2D via SmilesDrawer
- Fórmula molecular formatada com subscripts
- Property grid para propriedades físico-químicas
- Identificadores com links externos
- Classificações (ATC, SNOMED, SPOR)
- Progressive disclosure para informações adicionais
- Breadcrumb navigation
- Botão "Editar" para transição ao form

---

## Truncamento de Texto Longo em Tabelas

Nomes e descrições podem ocupar múltiplas linhas, quebrando o layout da tabela. Truncar com tooltip para manter linhas compactas.

### Padrão Jinja2

```html
<td>
    <a href="#"
       {% if item.nome|length > 100 %}title="{{ item.nome }}"{% endif %}>
        {{ item.nome[:100] }}{% if item.nome|length > 100 %}…{% endif %}
    </a>
</td>
```

### Regras

- **100 caracteres** é o limite padrão para nomes em tabelas de listagem.
- O `title` nativo do browser exibe o texto completo ao passar o mouse.
- Só adicionar `title` quando o texto for efetivamente truncado (evita tooltip redundante).
- Usar reticências `…` (caractere Unicode, não `...`) para indicar truncamento.
- O limite pode variar por contexto — colunas estreitas podem usar 60, descrições longas 150.

---

## Boas Práticas

### ✅ Fazer

- Separar claramente view (read-only) de edit (forms)
- Usar property grids para atributos tabulares
- Links para bases externas abrem em nova aba
- Breadcrumb para navegação contextual
- Progressive disclosure para informações secundárias
- Formatar dados apropriadamente (fórmulas, códigos, datas)

### ❌ Evitar

- Misturar inputs editáveis em view pages
- Campos de formulário desabilitados (use property grid)
- Excesso de informação sem organização
- Ausência de navegação de retorno
- Links internos sem contexto

---

## Integração com Backend

### Flask Route Pattern

```python
@app.route('/substances/<business_key>')
def substance_view(business_key):
    """View page para visualização de substância"""
    substance = db.query(Substance).filter_by(business_key=business_key).first_or_404()

    # Buscar atributos dinâmicos (EAV)
    attributes = db.query(SubstanceAttribute)\
                   .filter_by(substance_id=substance.id)\
                   .order_by(SubstanceAttribute.display_order)\
                   .all()

    # Buscar classificações
    classifications = get_substance_classifications(substance.id)

    return render_template('substance-view.html',
                         substance=substance,
                         attributes=attributes,
                         classifications=classifications)
```

### HTMX Enhancement

Para navegação sem page reload:

```html
<div class="table-row cursor-pointer"
     hx-get="/substances/{{ substance.business_key }}"
     hx-target="#main-content"
     hx-push-url="true">
    <!-- Conteúdo da linha -->
</div>
```

---

*Ver também*:
- [COMPOSITIONAL_FORMS.md](forms/COMPOSITIONAL_FORMS.md) - Padrões de formulários
- [PRESCRIPTION_COMPOSER.md](forms/PRESCRIPTION_COMPOSER.md) - Composer de prescrição
