# Drive — Views e UX

## 1. CMS — telas

| Arquivo | Função |
|---|---|
| `v6/resources/views/prefecture/contents/midias/edit.blade.php` | página do Drive (única tela do módulo) |
| `v6/resources/views/prefecture/contents/midias/form.blade.php` | grade de arquivos + upload + busca |
| `v6/resources/views/prefecture/contents/components/galerias/index.blade.php` | **mesmo componente**, usado dentro das abas de galeria dos outros módulos |

Não existe `index.blade.php` nem `create.blade.php` para `midias`: o Controller força a view `edit`.

### 1.1 `midias/edit.blade.php`

```blade
@extends('admin.layouts.app')
@section('content')
    <link rel="stylesheet" href="{{ url('assets/css/tooltip.css') }}">
    <link rel="stylesheet" href="{{ url('assets/css/prefecture/galeria/galeria.css') }}">

    <div class="box box-primary">
        <div class="box-header with-border">
            <h3 class="box-title">{{ $title }}</h3>
        </div>
        <div class="box-body">
            @include('prefecture.contents.midias.form')
        </div>
    </div>
@endsection

@section('other-scripts')
    @parent
    <script src="{{ url('assets/plugins/jquery-upload-files/jquery.uploadfile.min.js') }}"></script>
    <script src="{{ url('assets/js/custom/contents/midias/index.js') }}"></script>

    <script>
        chargeGallery();
        setupGallery();
    </script>
@endsection
```

- O título exibido (`{{ $title }}`) é **“Drive”**, vindo de `returnData('Drive', 'midias', null, [], $id)`.
- `chargeGallery()` e `setupGallery()` são chamados **sem argumentos**, o que faz o JS buscar
  `GET /cms/galeria/` (todos os arquivos) e registrar o upload sem `id`/`table` (modo Drive).

### 1.2 `midias/form.blade.php` (a grade)

```blade
<div id="multipleupload">Upload</div>

<div class="col-md-12">
    <div class="row">
        <div class="col-md-4">
            <label for="search-midia">Busca</label>
            <input type="text" id="searchInput" class="form-control" placeholder="Insira o nome da Imagem" onkeyup="searchImage()">
        </div>
    </div>
</div>

<h5 class="col-md-12 pl-4">Arquivos enviados</h5>

<div class="row">
    <div class="drive col-md-12" id="imageBox">
    </div>
```

Elementos relevantes:

| Elemento | Papel |
|---|---|
| `#multipleupload` | alvo do plugin `jquery-upload-files` |
| `#searchInput` | campo de busca por nome do arquivo |
| `.drive#imageBox` | container da grade de arquivos |
| `#loadingIndicator` | spinner exibido durante o carregamento dos lotes |

O componente `components/galerias/index.blade.php` tem exatamente o mesmo conteúdo, envolvido em
`<div id="galeria" class="tab-pane fade">` (aba de galeria dentro da tela de edição de outros
módulos).

### 1.3 Componente reutilizado por outros módulos

O mesmo `assets/js/custom/contents/midias/index.js` é incluído em:

| Arquivo | Contexto |
|---|---|
| `v6/resources/views/prefecture/contents/noticias/edit.blade.php` | galeria de notícias |
| `v6/resources/views/prefecture/contents/secretarias/edit.blade.php` | galeria de secretarias |
| `v6/resources/views/prefecture/contents/a-cidade/edit.blade.php` | galeria de “A Cidade” |
| `v6/resources/views/prefecture/contents/a-instituicao/edit.blade.php` | galeria de “A Instituição” |
| `v6/resources/views/prefecture/contents/sessoes/edit.blade.php` | galeria de sessões |
| `v6/resources/views/prefecture/contents/pauta-do-dia/edit.blade.php` | galeria da pauta do dia |
| `v6/resources/views/prefecture/contents/unidades-de-atendimento/edit.blade.php` | galeria de unidades |
| `v6/resources/views/prefecture/static-pages/edit.blade.php` | galeria de página estática |
| `v6/resources/views/prefecture/highlight/edit.blade.php` | galeria de destaque |
| `v6/resources/views/prefecture/calendar-events/edit.blade.php` | galeria de evento da agenda |

Nesses casos o JS é chamado com `id` e `table`, o que faz o upload ir para a pasta do conteúdo e
vincular o arquivo via `contents_id` / `static_pages_id` / `calendar_events_id`.

## 2. JavaScript do módulo

Arquivo: `v6/public/assets/js/custom/contents/midias/index.js`

### 2.1 Upload

```js
function setupUpload(id = null, table = null) {
    $("#multipleupload").uploadFile({
        multiple: true,
        showPreview: true,
        url: "/cms/galeria",
        acceptFiles: "image/jpeg, image/png, image/jpg",
        formData: {
            "_token": $('meta[name="csrf-token"]').attr('content'),
            "id": id,
            "table": table
        },
        onSuccess: function (files, data, xhr, pd) {
            adicionarImagemGaleria(data, true);
        }
    });
}
```

### 2.2 Listagem com rolagem infinita

```js
async function chargeGallery(id = null, column = null) {
    let index = 0;
    let batchSize = 50;
    let area = document.querySelector('.drive');

    let route = id ? `/cms/galeria/${column}/${id}` : `/cms/galeria/`

    let info = await myFetch(route);

    function setupInfiniteScrolling() {
        let sentinel = document.querySelector('.sentinel') || create_element({ tag: 'div', classes: 'sentinel' });
        area.appendChild(sentinel);

        let observer = new IntersectionObserver(entries => {
            if (entries[0].isIntersecting) {
                observer.disconnect();
                sentinel.remove();
                loadBatch();
            }
        }, { threshold: 1.0 });

        observer.observe(sentinel);
    }
```

Pontos importantes:

- A API **retorna todos os registros de uma vez**; o “lote de 50” existe apenas no front-end.
- Cada arquivo é validado com uma requisição `HEAD` para `{origin}/{path}`; se a resposta for
  redirecionada ou não for 200, o item é ocultado (`verifyAndLoadImages`).

### 2.3 Item da grade e ações

```js
function createButtonContainer(path, id) {
    let container = create_element({ tag: 'div', classes: 'option-container-box' });
    container.append(
        createButton('Copiar', 'btn-info', 'fa-clone', (event) => copiarLink(path, event)),
        createButton('Baixar', 'btn-success', 'fa-download', null, `/${path}`, true),
        createButton('Visualizar', 'btn-warning', 'fa-eye', null, `/${path}`),
        createButton('Excluir', 'btn-danger', 'fa-trash', (event) => deleteArchive(id, event))
    );
    return container;
}
```

Arquivos que não sejam `png`/`jpeg`/`jpg` são representados por um ícone
`fa fa-file-archive-o fa-4x` em vez da miniatura.

### 2.4 Exclusão

```js
async function deleteArchive(id, event) {
    event.preventDefault();
    let result = await myFetch(`/cms/galeria/delete/${id}`, 'post');

    if (result.success) {
        let elementToRemove = document.querySelector(`.id_${id}`);
        if (elementToRemove) elementToRemove.remove();
        toastr.success(result.success);
    } else {
        toastr.error(result.erro);
    }
}
```

> A exclusão remove **apenas o registro** (`archives`): o arquivo físico continua em
> `public/storage/content/midias/`. Não há remoção do binário.

## 3. O que o usuário vê

**Operador:** uma página com o título “Drive”, o botão de upload múltiplo (com barra de progresso e
pré-visualização), o campo “Busca” e a grade de arquivos (miniatura, nome e quatro botões).

**Cidadão:** nada. Não há página pública de mídia.

## 4. Exportações

Não há exportação no módulo. Os arquivos são acessíveis individualmente por “Baixar” e “Copiar”.

## 5. Assets

| Arquivo | Papel |
|---|---|
| `v6/public/assets/js/custom/contents/midias/index.js` | upload, listagem, busca, ações |
| `v6/public/assets/js/custom/utilities/functions.js` | `myFetch`, `create_element` |
| `v6/public/assets/plugins/jquery-upload-files/jquery.uploadfile.min.js` | upload múltiplo |
| `v6/public/assets/css/prefecture/galeria/galeria.css` | estilo da grade |
| `v6/public/assets/css/prefecture/galeria/spinner.css` | spinner de carregamento |
| `v6/public/assets/css/sortable.css` | estilos de ordenação |
| `v6/public/assets/css/tooltip.css` | tooltips dos botões |
