# Portal de importação Excel → Clarizen WorkItem Portal web MVP para enviar um arquivo `.xlsx` e criar ou atualizar **WorkItems** no Planview Adaptive Work (Clarizen), usando [@arco/clarizen-lib](https://gitea.rivuslab.com/leonardotoniolo/clarizen-lib). ## Pré-requisitos - [Node.js](https://nodejs.org/) 18 ou superior - Acesso ao Gitea da Rivus Lab (para instalar `clarizen-lib` via Git) - Chave de API do Adaptive Work ## Instalação ```bash cd update_xlsx npm install ``` Se o repositório `clarizen-lib` for privado, configure autenticação Git (SSH ou token HTTPS) antes do `npm install`. ## Configuração ```bash cp .env.example .env ``` Edite `.env` na raiz do projeto: ```env CLARIZEN_BASE_URL=https://api.clarizen.com CLARIZEN_API_KEY=sua_chave_aqui PORT=3000 ``` Alternativa aceita: `API_KEY_AW` em vez de `CLARIZEN_API_KEY`. O carregador também tenta `../clarizen-lib/.env` se existir (desenvolvimento local no monorepo ARCO). ## Executar ```bash npm start ``` Abra [http://localhost:3000](http://localhost:3000). | Comando | Descrição | | -------- | --------- | | `npm start` | Sobe o portal (Express + UI estática) | | `npm run check:clarizen` | Verifica se a lib foi instalada e compilada (`OK Clarizen`) | ## Formato do Excel - **Linha 1:** cabeçalhos — pode usar o **nome do campo** na API ou o **label** exibido no Adaptive Work (ex.: `ExternalID` ou `External ID`, `CreatedOn` ou `Created On`). - **Linhas 2+:** um WorkItem por linha. - Colunas vazias são ignoradas. Na importação, o portal consulta o metadata de `WorkItem` no Clarizen e resolve cada cabeçalho para o nome técnico do campo. Colunas não reconhecidas retornam erro 400 antes de criar/atualizar registros. ### Cabeçalhos: nome vs label | Cabeçalho no Excel | Campo enviado à API | |--------------------|---------------------| | `Name` | `Name` | | `Name` (label) | `Name` | | `ExternalID` | `ExternalID` | | `External ID` | `ExternalID` | | `Created On` | `CreatedOn` | Após a importação, a UI mostra o mapeamento **Excel → API** (`headerMapping`). ### Create vs update | Situação | Ação | | -------- | ---- | | Linha **sem** valor em `ExternalID` (ou coluna mapeada para ele) | **Criar** novo WorkItem | | Linha **com** `ExternalID` preenchido | **Atualizar** WorkItem existente com esse ID externo | Aliases de cabeçalho aceitos: `ExternalId`, `External ID`, `externalId` → `ExternalID`. ### Exemplo (labels) | Name | External ID | Description | Start Date | |------|-------------|-------------|------------| | Tarefa nova | | Descrição da tarefa | 2026-06-01 | | Tarefa existente | EXT-001 | Descrição atualizada | 2026-06-15 | ### Exemplo (nomes API) | Name | ExternalID | Description | StartDate | |------|------------|-------------|-----------| | Tarefa nova | | Descrição da tarefa | 2026-06-01 | | Tarefa existente | EXT-001 | Descrição atualizada | 2026-06-15 | A lógica de create/update está em `src/import/config.ts` e `src/import/processor.ts` para facilitar alterações futuras. ## Estrutura ```text update_xlsx/ ├── .env.example ├── public/ # UI (HTML, CSS, JS) ├── src/ │ ├── server.ts │ ├── clarizen/ # cliente e auth │ ├── excel/ # leitura xlsx │ ├── import/ # create/update, field-headers (name/label) │ └── routes/ # POST /api/import └── README.md ``` ## Limitações (MVP) - Apenas entidade **WorkItem** (sem seleção na UI). - Campos de referência (`Manager`, `Parent`, etc.) podem exigir formato específico da API; o MVP envia valores escalares simples do Excel. - Processamento sequencial linha a linha (sem bulk API). ## Problemas comuns | Sintoma | O que fazer | | -------- | ------------ | | Erro ao clonar `clarizen-lib` no `npm install` | Verificar login SSH/HTTPS no Gitea | | Banner amarelo na página | Conferir `.env` e `npm run check:clarizen` | | `WorkItem não encontrado para ExternalID=...` | O ID externo não existe no tenant; use create (linha sem ExternalID) ou corrija o valor | | Erro 400 no upload | Arquivo deve ser `.xlsx` / `.xls` com cabeçalho e ao menos uma linha de dados | | `Colunas não reconhecidas: ...` | Cabeçalho não bate com nome nem label de campo WorkItem no seu tenant | | `Label ambíguo` | Dois campos distintos compartilham o mesmo label — use o nome técnico do campo |