# 07 - Front-end

> O front do Genesis **não é um SPA de framework**: é HTML renderizado no servidor (PHP + League Plates) dentro de um shell de tema, com uma camada de "SPA parcial" via axios + jQuery que troca fragmentos de DOM. Este capítulo explica o tema, o pareamento view+JS, o protocolo de respostas, o sistema Luz, modais, formulários, i18n e os padrões mobile.
>
> Relacionados: [01 - Arquitetura](01-arquitetura-geral.md) (GalaxiaRoute), [10 - Guia de Desenvolvimento](10-guia-de-desenvolvimento.md) (como criar uma tela), [12 - Armadilhas](12-armadilhas-conhecidas.md).

## 1. Renderização: do controller ao browser

1. O controller do Sinal monta HTML: `$response['html'] = $this->galaxiaRoute->visual('html/minhaview', $data, [])`.
2. `GalaxiaRoute` empacota em `galaxiaHtml` e chama `viewTheme('dashboard', ...)`.
3. O tema ativo (`genezes`, hardcoded em `GalaxiaRoute.php`) aplica o layout: `dashboard.php` → `_theme.php` → o conteúdo entra no root **`#genezes_app_root`**.

**Atenção:** `templates/` na raiz do repo NÃO é o tema (só guarda o template de PRD). Os temas visuais ficam em `controladores/_Src/galaxia_themes/` (`genezes` ativo; `goldie`, `basic`, `metronic*` legados).

O `_theme.php` do genezes (~3.000 linhas) contém: taskbar estilo Windows com Start Menu, seletor de idioma, badge de desenvolvedor, efeito de estrelas, suporte PWA (pull-to-refresh, manifest) e o carregamento de todos os scripts globais. Variáveis expostas ao JS: `window.systemUrl`, `window.googleMapsKey`, etc.

## 2. Stack de bibliotecas

Sem bundler e sem transpilação — scripts estáticos carregados no `_theme.php`:

- **Base:** jQuery + jQuery UI, Bootstrap 5 (com polyfill de `$.modal()`), **axios** (transporte de todas as chamadas Galaxia).
- **UI:** Tailwind (CSS gerado manualmente via CLI; config em `tailwind.config.js`) + Flowbite, Font Awesome, SweetAlert2 (confirmações), toastr/Toastify (notificações), Quill (rich text), intro.js/driver.js (tours).
- **Dados/gráficos:** ApexCharts (principal), Chart.js, dayjs.
- **Condicionais por rota:** Google Maps + Turf.js (só `/logistica/central|admin`), Monaco (só `/c/central`), powerbi-client.
- **Realtime:** Scaledrone (ver [08 - Integrações §7](08-integracoes.md)).
- React/ReactFlow constam no `package.json` mas só entram em telas específicas — não há montagem global.

Scripts do motor: `controladores/_Src/func.js` (protocolo Galaxia), `main.js` (handlers de eventos delegados), `db.js` (IndexedDB), `maps.js` (logística).

## 3. View + JS pareado (o padrão fundamental)

```
sinais/<Sinal>/
├── <Sinal>.php          # controller
├── html/<view>.php      # markup
├── html/<view>.js       # JS pareado — carregado e versionado automaticamente
└── forms/<acao>_<obj>.php
```

`GalaxiaRoute::visual()` renderiza a view e, **se existir um `.js` de mesmo nome no mesmo diretório**, anexa `<div data-novoscript="1" data-script=".../view.js?v=<filemtime>">`. No cliente, `loadDynamicScripts()` (`func.js`) varre `[data-script]`, remove scripts duplicados de mesmo `src` e injeta o `<script>` — depois de **cada** resposta AJAX que insere HTML.

Consequências:
- **Cache-bust automático**: salvar o `.js` muda o `filemtime` → novo `?v=` → browser baixa de novo. (Exceção: módulos com `<script src>` manual, como os modais de metas do sistema antigo, exigem bump manual de `?v=N`.)
- A view "index" do Sinal usa o **nome do controller em minúsculo** (`Edicaorota` → `html/edicaorota.php`); as demais têm nome livre.
- O JS pareado deve ser **idempotente** (pode ser reinjetado após atualizações parciais).

## 4. O protocolo de resposta (RequestGalaxiaAct)

Toda ação (form, botão, modal, socket) devolve um JSON de **diretivas declarativas** que `RequestGalaxiaAct()` (`func.js`) aplica no DOM. É o contrato entre `galaxiaResposta()` no backend ([01 §5](01-arquitetura-geral.md#5-galaxiaroute-a-api-fluente)) e o browser:

| Diretiva (JSON) | Backend (`GalaxiaRouteRespostas`) | Efeito |
|---|---|---|
| `galaxiaMissaoUpdate[]` | `reencaminheLuz()` | Patch parcial de UI (Luz, §5) |
| `modalLoad` | — | Conteúdo em modal |
| `elementsHtml[]` | `substituirConteudo()` | `$(tag).html(html)` |
| `inserirConteudo[]` | `inserirConteudo()` | append |
| `removerElemento[]` / `hideElements[]` | `removerElemento()` | remove/fadeOut |
| `preencherCampo[]` | `preencherCampo()` | `$(tag).val()` |
| `chamarFuncoes[]` | `chamarFuncao(fn, dados)` | invoca `window[fn](dados)` |
| `closeModal` / `doNotCloseModal` | `fecharModal()` | fecha modal |
| `notifications[]` | — | toastr |
| `proximaRota` / `trocarUrl` | `proximaRota()` / `trocarUrl()` | navegação (pushState) |
| `reload` | `atualizarPagina()` | recarrega |
| `salvarElementoEmCache[]` | — | grava HTML no IndexedDB (cache SPA) |

Transporte: `SendGalaxiaGet/Post` (axios + token CSRF por request). Callbacks JS chamados via `chamarFuncao` precisam estar em `window` — funções dentro de IIFE devem ser expostas explicitamente (padrão do projeto: `window.minhaFn = minhaFn`).

## 5. Luz: atualização parcial de UI

O Luz é o "partial/turbo-frame" do Galaxia:

- **Backend:** cada bloco atualizável é renderizado com uma âncora `data-gxloadtag` (hash da rota) via `getTagAutoRefresh()`. O controller expõe `Luz_<Nome>(?array $data)`. Para atualizar: `reencaminheLuz('<Nome>', ['luzId' => $id, ...], $data)` re-executa o método e devolve `galaxiaMissaoUpdate[]`.
- **Cliente:** `doMissaoUpdate()` localiza o elemento pela tag e aplica a ação: `criacao` insere antes do primeiro bloco, `edicao` faz `replaceWith`, `remover` remove.
- **Regra de loops:** cada instância repetida precisa de `luzId` único (`autoId<luzId>`), senão o patch acerta o elemento errado.
- Componentes podem ser **registrados globalmente** em `componentes/Sinais/Sinais.php` para serem disparados por qualquer Sinal sem conhecer o caminho.
- O realtime (Scaledrone) publica **as mesmas diretivas** — o servidor consegue atualizar a UI de outros usuários com o mesmo vocabulário.

## 6. Modais e painéis laterais

- **Backend:** `$galaxia->abrirModal($sinal, ['estrela' => ..., 'startSinal' => ...])['html']` gera os atributos `data-galaxia-remote/sinal/estrela/startsinal` + `data-modal-size`.
- **Cliente:** handler global em `main.js` abre `bootstrap.Modal` e chama `openGalaxiaModal()`, que monta `/missao/<estrela>/<startSinal>/<sinal>?fromRequest=modal...` e injeta a resposta.
- **Painel lateral** = modal com `data-modal-size="rightSide"` (350px ancorado à direita) — não existe componente separado.
- **Mobile:** `innerWidth <= 650` aplica `.modalMobile`/`.bg-white` automaticamente.
- Evite chamar `openGalaxiaModal()` manualmente via JS — gere os atributos pelo backend.

## 7. Formulários

- **Só** via `$this->galaxiaRoute->visualFormulario('forms/arquivo', $data, [])` — nunca `<form>` manual. O framework embrulha a view em `<fieldset name="sendToGalaxia">` com header/footer (botão de submit configurado por `$data['opcoesDoBotao']`), injeta tag única, `galaxiaRouteId` e o JS pareado.
- As views de `forms/` contêm **apenas inputs** — zero lógica.
- Submit vira POST AJAX (`prepareGalaxiaForm` → `SendGalaxiaPost`); a resposta segue o protocolo do §4 (tipicamente `fecharModal()` + `reencaminheLuz`).
- Upload de arquivos exige `$data['filesUpload'] = true`.
- Campos com envio on-change: classe `.sendToGalaxiaOnChange`.

## 8. i18n: show() por md5 do texto

A função global `show($texto)` (`system/base_config/Helpers/functions.php`):

- Recebe o **texto em português literal**; a chave de tradução é `md5($texto)`. **Nunca** invente chave legível (documentação antiga que sugere snake_case está desatualizada).
- Idioma vem do cookie `GalaxiaLinguagem` (default `pt-br`; seletor na taskbar). Três idiomas: `pt-br`, `en`, `es`.
- **Runtime por setor:** os JSONs efetivos ficam em `/home/goldie/www/galaxia/linguagens/<setor>/<lang>.json` (setor = `sectorData->pathname`) — os arquivos `linguagens/*.json` do repo são a base global.
- **Auto-harvest:** se a chave não existe, `show()` **cria a entrada** com `original`, `traducao` (= o próprio PT) e `solicitacoes[]` (arquivo que chamou). Por isso strings novas **só aparecem nos JSONs depois de renderizadas no browser** — validar i18n exige abrir a tela.
- Ao adicionar strings: escrever `show('Texto em português')` no código e preencher a tradução nas 3 línguas nos JSONs.

## 9. GBlocos e Workspaces (page builder e boards)

- **GBlocos** (`componentes/Missoes/GBlocos/sinais/`) é o catálogo de blocos de um construtor visual (Drawflow, atributos `df-*`). Cada bloco tem 3 views: `campos.php` (painel de config), `elemento.php` (nó no canvas do editor) e `estrutura.php` (HTML final). Blocos: Bloco (container), Tabela, Tabelamonday, Formulario, CampoTexto/CampoSelect, Botao, Modal, Nav/NavItem/NavConteudo, Cabecalho, Accordion, Filtros, Condicao, Whiteboard, Notas, AlertasHtml, Html, AbrirPortal, Laboratorio (vincula bloco a lógica), Apis, Novavariavel, ConteudoPaginaBase/EstruturaCrudBase. O editor em si vive na Missão `Construcao`.
- **Workspaces** implementa boards estilo Monday: sinais `Boards`, `Nos` (cartões), `Divs`, `Paginas`, `Manager`, `Rotas` (rotas internas de nós) e `Luz` (roteador de layout por `layoutDoMomento`). A atualização de cards usa `reencaminheLuz` com `luzAcao` criacao/edicao/remover.

## 10. PWA, cache SPA e navegação

- PWA instalável: `system/manifest.json` + `system/sw.js` (service worker mínimo, sem cache offline real) + pull-to-refresh próprio no tema.
- **Cache SPA local:** IndexedDB `galaxiaStoreLocal` (`db.js`), alimentado pela diretiva `salvarElementoEmCache`. Se uma tela "não atualiza" após deploy, esse cache é suspeito ([Armadilha do PDV](12-armadilhas-conhecidas.md)).
- Navegação: links full-page via `proximaRota()`; navegação sem reload via `trocarUrl` + patches de DOM (pushState). O "menu" é o Start Menu da taskbar (itens do app integrado, busca de rotas, atalhos configuráveis).

## 11. Isolamento de CSS (app integrado)

Para embutir o Genesis em apps hospedeiros (Ramify), `isolate_css.js` (PostCSS + `postcss-prefix-selector`) gera `style.bundle.isolated.css` com todo seletor prefixado por `.appGenezes`. `$appIntegrado==1` desliga assets duplicados e injeta os do host. Flags: `disableGenAssets/disableGenCss/disableGenScripts`.

## 12. Padrões mobile

- Viewport travado sem zoom; `overscroll-behavior-y: contain` (pull-to-refresh próprio).
- **Largura real ≠ largura do body** no tema genezes mobile: body 375px, `window.innerWidth` 512 — use 512 para elementos full-width; gráficos Gantt (ApexCharts rangeBar) precisam clampar `xaxis.min/max` na janela real via `matchMedia`.
- Bloquear scroll horizontal: `overflow-x: clip` (não `hidden`, que vira scroll container e quebra sticky/scroll vertical) — técnica adotada nas telas novas; temas antigos usam `overflow-x-hidden` do Tailwind.
- SortableJS (listas drag): ver armadilhas 16 ([12](12-armadilhas-conhecidas.md)) — classes empilhadas, long-press + `touch-action: none`.
- Inputs numéricos com drag/touch para incrementar (handlers passivos em `func.js`).

## 13. Checklist de tela nova (resumo)

1. View em `html/<nome>.php` + JS pareado `html/<nome>.js` (view index = nome do controller em minúsculo).
2. Strings via `show('Texto PT')`; traduzir nas 3 línguas; validar no browser.
3. Formulários via `visualFormulario` + `forms/`; ações via `acaoElemento`/`abrirModal`.
4. Blocos atualizáveis expostos como `Luz_<Nome>` com `luzId` único em loops.
5. Cores via variáveis do tema (`var(--bg-*)`) — nunca hardcoded.
6. Testar no viewport mobile (512px real) e com o tema dark.

O passo a passo completo está em [10 - Guia de Desenvolvimento](10-guia-de-desenvolvimento.md).
