Aprendizados do mindscrum 0.7.0-0.9.0. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
4.3 KiB
| id | title | domain | tags | status | severity | created_at | updated_at | applies_to | related_records | |||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| KB-PLUGIN-043 | GLPI 11 — Padrões de UX de card kanban em plugin: anatomia do card nativo, own_ticket, is_private, TTR, cores de prioridade e gotchas de CSS | plugin-dev |
|
active | medium | 2026-07-13 | 2026-07-13 |
|
|
GLPI 11 — Padrões de UX de card kanban em plugin (aprendizados mindscrum 0.7–0.9)
Anatomia do card do Kanban NATIVO (receita para replicar)
Fonte: Project::getKanbanColumns() + Vue Kanban-Card (compilado em
public/build/vue/vue-sfc/). O card nativo carrega:
title; descrição viaRichText::getTextFromHtml($content)(em_metadata);- link do pai ("Subtask of X") →
getFormURLWithIDdo pai; - contador "x / y tasks complete" a partir de
_steps(percent_done === 100); - barra de progresso
Html::progress(100, $percent)(Tabler.progress); _team(badges de membros),due_date=Html::convDateTime(plan_end_date);_form_link; hooks de pluginPRE/POST_KANBAN_CONTENTeKANBAN_ITEM_METADATA.
Replicar server-side com queries EM LOTE (1 p/ equipes, 1 p/ contagens
COUNT/SUM(GROUPBY), 1 p/ pais via join, 1 p/ nomes de usuários) — sem N+1.
Técnicos elegíveis para atribuição (dropdown nativo)
User::getSqlSearchResult(false, 'own_ticket', $entities_id, 0, [], '', 0, $limit)
→ retorna o iterator pronto. 'own_ticket' é o right que o próprio core usa
para o ator "atribuído" de Ticket (exclui contas só-requisitantes). Para equipe de
projeto, right 'all' na entidade do projeto.
Followup privado (nota "só técnico")
ITILFollowup nativo: campo is_private; right followup com
ITILFollowup::SEEPRIVATE. Visibilidade: privada aparece só para o AUTOR ou quem
tem SEEPRIVATE — replicar esse filtro ao listar followups por fora da API padrão.
time_to_resolve (prazo/SLA manual)
TicketNÃO tem data prevista de início nativa. Datas do chamado:date(abertura),time_to_resolve,time_to_own(+internal_*). Início planejado vive nas tasks (TicketTask.begin/end,ProjectTask.plan_*).time_to_resolveaceita valor manual noadd()/update()— herdarplan_end_dateda task de projeto ao criar chamado dá visão de estouro "de graça".- ⚠️ Gotcha: o core DESCARTA SILENCIOSAMENTE
time_to_resolveanterior à data de abertura (update()retorna true e o campo fica null). Em testes, usar abertura retroativa (dateno input do add) para simular estouro. - Barrinha de "SLA":
% = (now - date) / (time_to_resolve - date), capado em 100.
Cores/ícones de prioridade
Cores nativas configuráveis em $CFG_GLPI['priority_1'..'priority_6']
(tons de vermelho claro — são cores de FUNDO). Para UI discreta, preferir ícone
Tabler + tooltip (getPriorityName): chevrons (baixas), ti-equal (média),
ti-exclamation-circle/ti-alert-triangle/ti-alert-octagon (altas).
Gotchas de CSS/JS em modal custom
- Keyframe que anima
transformSUBSTITUI o transform do elemento durante a animação: se a centralização usatranslate(-50%,-50%), o elemento "nasce" deslocado e pula ao final. Solução: centralizar com flex no wrapper (position:fixed; inset:0; display:flex) e deixar o keyframe só com scale. - Flatpickr com
altInput: estilizar viaaltInputClass(o input visível é NOVO; regras no input original não pegam) — senão o layout quebra. .catch()defetch(...).then(render)captura exceções do RENDER também — um erro de JS no template do modal aparece como se fosse falha de rede/endpoint. (Sintoma clássico: "modal funciona pra um itemtype e não pra outro".)- Popover/sub-modal empilhado sobre modal custom: z-index acima (1070+) e Esc tratado em pilha (fecha o de cima primeiro).
Espelhamento de descrição (padrão mindscrum)
Spawn task→chamado importa título + content (texto via RichText) + prazo. A
seção "Descrição" do modal aponta para a FONTE: projeto → Project.content;
chamado → ProjectTask.content da task de origem (fallback: do chamado). Editar
grava na fonte via update() — allowlist de itemtype/campo no endpoint.