# Drive — Regras de Negócio e Portabilidade

## 1. Regras de negócio observáveis

| # | Regra | Onde está |
|---|---|---|
| R1 | O Drive não tem entidade própria: é a modalidade de upload sem `table`/`id` | `v6/app/Http/Controllers/Prefecture/ArchiveController.php:111-114` |
| R2 | Arquivos sem dono são gravados em `storage/content/midias` | idem |
| R3 | O `name` do arquivo é aleatório (`uniqid` + 5 caracteres + extensão) | `v6/app/Http/Traits/FileTrait.php:78-82` |
| R4 | O upload cria versões `_min` (200px), `_med` (380px) e `_larg` (1480px) da imagem | `FileTrait::compress` |
| R5 | O cliente restringe o upload a `image/jpeg`, `image/png`, `image/jpg` | `assets/js/custom/contents/midias/index.js` (`acceptFiles`) |
| R6 | A listagem do Drive mostra **todos** os arquivos do sistema, sem filtro | `getArchivesFromGalleryOrDriver` sem `id` |
| R7 | Os arquivos são ordenados do mais novo para o mais antigo (`id DESC`) | idem |
| R8 | Arquivos com `extension` nulos são excluídos da listagem | `where('extension', '!=', null)` |
| R9 | A exclusão é lógica (soft delete) e **não** remove o arquivo físico | `Archive::destroy` |
| R10 | A busca é client-side, sobre o nome exibido | `filterImages` |
| R11 | Cada item tem Copiar, Baixar, Visualizar e Excluir | `createButtonContainer` |
| R12 | O módulo aparece no menu como “Drive”, mas a categoria se chama “Mídias” | `MenuServiceProvider` + `CategoryContentTableSeeder` |

## 2. Acoplamentos

### Dependências de outros módulos

| Módulo | Dependência |
|---|---|
| Galeria de conteúdos | `Content`, `CategoryContent`, `ContentController@returnData` |
| Galeria de eventos | `CalendarEvent`, `CategoryCalendarEvent` |
| Galeria de páginas estáticas | `StaticPage` |
| Destaques | `archives.highlight_id` -> `highlights.id` |
| Licitações / contests | `archives.biddings_id`, `contests_has_archives` |
| Manual do usuário | `archives` com `contents_id` nulo e path `storage/manual-usuario/` (87 dos 91 registros do banco) |
| Configuração de layout | tabela `layouts` (blocos do rodapé do `modelo5`) |

### Dependências deste módulo em outros módulos

O JavaScript do Drive (`assets/js/custom/contents/midias/index.js`) é carregado por **10 telas**
de edição de outros módulos (notícias, secretarias, agenda, páginas estáticas, destaques, etc.).
Qualquer alteração no arquivo afeta todos esses módulos.

## 3. Armadilhas e riscos técnicos

| # | Armadilha | Evidência | Impacto |
|---|---|---|---|
| A1 | A listagem do Drive não tem escopo: devolve todos os `archives`, inclusive de outros módulos | `getArchivesFromGalleryOrDriver` sem `id` | o operador vê arquivos de terceiros; risco de exclusão acidental |
| A2 | A rota `GET /galeria/{column?}/{id?}` injeta `column` diretamente na query | `->where($colunm, $id)` | nome de coluna arbitrário (o valor vem da URL) |
| A3 | Não há validação de tipo/tamanho no servidor | `ArchiveController@saveArchivesFromGalleryOrDriver` | upload de qualquer arquivo; risco de execução de conteúdo malicioso servido pelo `storage` |
| A4 | A exclusão não remove o arquivo físico | `deleteArchives` chama apenas `Archive::destroy` | espaço em disco cresce; arquivos continuam acessíveis por URL |
| A5 | O nome original do arquivo é perdido (`getFileName` gera um nome aleatório) | `FileTrait::getFileName` | o operador não reconhece o arquivo pelo nome |
| A6 | A “busca” no formulário (`onkeyup="searchImage()"`) aponta para uma função que **não existe** no JS do módulo | `midias/form.blade.php` vs `assets/js/custom/contents/midias/index.js` (que define `setupSearch`, não `searchImage`) | erro de JavaScript no console ao digitar na busca |
| A7 | O upload registra o arquivo mas **não** cria nenhum `contents`; a categoria `midias` fica sem utilização | `ContentController@store` nunca é chamado pelo Drive | a categoria `midias` serve apenas de rótulo |
| A8 | `GET /cms/conteudos/midias` redireciona para o dashboard (não há listagem de conteúdo para `midias`) | `genericIndex` sem caso `midias` | link colado manualmente não funciona |
| A9 | A API de listagem retorna a collection inteira; o “lote de 50” é só front-end | `chargeGallery` | volume alto de JSON e de requisições `HEAD` |
| A10 | Uma requisição `HEAD` é disparada **por arquivo** para verificar existência | `verifyAndLoadImages` | N+1 de rede na tela do Drive |
| A11 | `where('extension', '!=', null)` no PostgreSQL exclui as linhas com extensão nula (comparação com NULL é NULL) | mesma consulta | comportamento dependente do SGBD |
| A12 | `ArchiveController` não aplica `check.route.permission` nem `has_access()` | construtor só instancia o repositório | qualquer usuário logado no CMS manipula arquivos |
| A13 | O JS é compartilhado por 10 telas; uma correção de bug no Drive pode quebrar a galeria de outros módulos | `assets/js/custom/contents/midias/index.js` | risco de regressão amplo |
| A14 | `updateArchivesOrder` recebe `related_type` e monta `Archive::where('id', $order['id'])->where($relatedType, $relatedId)` | `ArchiveController@updateArchivesOrder` | nome de coluna vindo do cliente |
| A15 | `type_size_id`, `width` e `height` ficam nulos no Drive, mas o `ContentPresenter` usa esses dados para montar imagens | `ContentPresenter::getArchiveContentHighlightOrThumbnail` | imagens do Drive não têm tamanho declarado |

## 4. Código duplicado entre v5 e v6

| Trecho | v5 | v6 | Observação |
|---|---|---|---|
| Telas do Drive | `v5/resources/views/prefecture/contents/midias/` | `v6/resources/views/prefecture/contents/midias/` | mesma estrutura (`form.blade.php` + `edit.blade.php`) |
| JS da galeria | `v5/public/assets/js/custom/contents/midias/index.js` | idem | idêntico |
| Controller de arquivos | `v5/app/Http/Controllers/Prefecture/ArchiveController.php` | `v6/app/Http/Controllers/Prefecture/ArchiveController.php` | mesma estrutura; a v6 adicionou `updateArchivesOrder` e `updateArchiveTitle` |
| Rotas | `v5/routes/basepro.php:381` (grupo `galeria`) | `v6/routes/basepro.php:384` | mesmas 5 rotas |
| Resource | `resources.slug = midias` | `resources.slug = drive` | renomeado em `v6/database/seeds/AjustesMigracao.php:81-90` |
| Módulo | inexistente | `modulos` id 30 `drive` | **novo na v6** |

## 5. Onde a v6 divergiu da v5

- **Renomeação da funcionalidade**: o resource `midias` passou a se chamar `drive` (`AjustesMigracao.php`),
  o módulo `drive` foi criado na tabela `modulos` e o item de menu passou a exibir o texto “Drive”
  (antes `v5/app/Providers/MenuServiceProvider.php` usava outro rótulo para o mesmo `case 'midias'`).
- **Rota de ordenação**: `updateArchivesOrder` e `updateArchiveTitle` existem na v6 e permitem
  renomear e reordenar arquivos; a v5 não tinha esses métodos no `ArchiveController`.
- **Demais**: o comportamento de upload, listagem e exclusão é o mesmo.

## 5.1 Checklist de paridade v6 -> v7

| Verificação | Como conferir |
|---|---|
| Upload múltiplo com pré-visualização | enviar 3 arquivos e observar a barra de progresso |
| Arquivo acessível por URL pública | “Copiar” e abrir a URL em aba anônima |
| Download e visualização | “Baixar” e “Visualizar” |
| Exclusão remove o item da grade | clicar em “Excluir” e recarregar a página |
| Busca por nome | digitar na busca e filtrar a grade |
| Galeria dos demais módulos continua funcionando | abrir a edição de uma notícia e anexar uma imagem |
| Pasta física preservada | conferir `public/storage/content/midias` |

## 6. Regras de equivalência funcional (o que não pode mudar na v7)

1. O operador envia vários arquivos de uma vez e vê a pré-visualização.
2. Os arquivos ficam disponíveis para os demais módulos (galeria de conteúdo).
3. “Copiar” entrega uma URL pública funcional do arquivo.
4. “Baixar” entrega o arquivo e “Visualizar” abre em nova aba.
5. “Excluir” remove o item da grade.
6. A busca por nome continua filtrando a grade sem recarregar a página.
7. A tela do Drive continua acessível pelo menu lateral, com o rótulo “Drive”.
