diff --git a/README.md b/README.md index c929d31..96b944f 100644 --- a/README.md +++ b/README.md @@ -1,59 +1,57 @@ # Sistema Arte -Plugin WordPress para gerenciamento de tarefas integrado à API Vikunja. +Plugin WordPress para gerenciamento de demandas de arte. ## Descrição -O **Sistema Arte** é um plugin para WordPress que permite a criação e acompanhamento de demandas de artes gráficas, integrando-se diretamente à API Vikunja para gerenciamento de tarefas. O plugin oferece um formulário customizado para solicitação de demandas e exibe uma lista das tarefas pendentes do projeto configurado. +O **Sistema Arte** é um plugin para WordPress que cria um sistema autônomo para gerenciamento de demandas de arte dentro do painel de administração. Ele permite que usuários enviem solicitações através de um formulário e que os administradores gerenciem o fluxo de trabalho usando um quadro Kanban. ## Funcionalidades -- Formulário customizado para solicitação de artes, com campos obrigatórios: - - Título da arte - - Nome completo do solicitante - - Secretaria - - Telefone/WhatsApp - - Detalhes da solicitação - - Data de entrega (padrão: hoje + 7 dias às 17h) - - Prioridade -- Integração direta com a API Vikunja para criação e listagem de tarefas -- Exibição das demandas pendentes em tabela -- Validação de campos obrigatórios e feedback de sucesso/erro -- Interface moderna utilizando Tailwind CSS +- **Formulário de Solicitação:** Um formulário customizado para solicitação de artes, com campos para: + - Título, nome do solicitante, secretaria, contato. + - Detalhes da solicitação e anexo de arquivos. + - Data de entrega e nível de prioridade. +- **Gerenciamento no WordPress:** + - As demandas são salvas como um tipo de post personalizado ("Demandas de Arte"). + - Utiliza uma taxonomia customizada ("Status") para controlar o fluxo. +- **Quadro Kanban:** + - Um painel de administração visual com as colunas: `Demanda`, `Fazer`, `Fazendo` e `Feito`. + - Funcionalidade de arrastar e soltar (drag-and-drop) para mover as demandas entre as colunas e atualizar seu status. +- **IDs Sequenciais:** + - Sistema de ID personalizado e sequencial (ex: A001, A002) para fácil identificação das demandas. +- **Lista de Demandas Pendentes:** + - O shortcode exibe uma tabela com todas as demandas que não estão com o status "Feito". +- **Interface Moderna:** + - Utiliza Tailwind CSS para o formulário e um design limpo para o quadro Kanban. ## Instalação 1. Faça upload da pasta `sistema-arte` para o diretório `wp-content/plugins/` do seu WordPress. 2. Ative o plugin no painel do WordPress. -3. Configure as variáveis de integração com a API Vikunja em `includes/config.php`: - - `$apiBase`: URL base da API - - `$token`: Token de acesso - - `$projectId`: ID do projeto no Vikunja +3. Após a ativação, o menu "Demandas de Arte" aparecerá no painel de administração. ## Uso Adicione o shortcode `[Sistema-Arte]` em qualquer página ou post para exibir o formulário de solicitação e a lista de demandas. ## Estrutura dos Arquivos - `sistema-arte.php`: Arquivo principal do plugin -- `includes/config.php`: Configurações da API -- `includes/api.php`: Funções de integração com a API Vikunja - `includes/templates/form.php`: Template do formulário de solicitação - `includes/templates/tasks.php`: Template da lista de tarefas -- `includes/assets/script.js`: Scripts JS para manipulação do formulário +- `includes/assets/script.js`: Scripts JS para o formulário (máscara de telefone) +- `includes/assets/kanban-board.js`: Scripts JS para a funcionalidade do quadro Kanban +- `includes/assets/kanban-style.css`: Estilos CSS para o quadro Kanban ## Dependências - [Tailwind CSS](https://tailwindcss.com/) (via CDN) - jQuery (WordPress padrão) +- jQuery UI Sortable (WordPress padrão, para o Kanban) +- jQuery Mask Plugin (via CDN, para o campo de telefone) ## Segurança - Utiliza `wp_nonce_field` para proteção contra CSRF - Sanitização e validação de todos os campos do formulário - Escapando de todas as saídas para evitar XSS -## Observações -- O plugin depende de um projeto e token válidos na API Vikunja. -- O usuário do token precisa de permissão de escrita no projeto configurado. -- Em caso de erro de permissão, dados inválidos ou projeto não encontrado, mensagens detalhadas são exibidas ao usuário. - ## Autor Marco Antonio Vivas --- -Plugin desenvolvido para integração com o sistema Vikunja, facilitando a gestão de demandas de artes no WordPress. \ No newline at end of file +Plugin desenvolvido para facilitar a gestão de demandas de artes diretamente no WordPress. \ No newline at end of file diff --git a/includes/assets/kanban-board.js b/includes/assets/kanban-board.js new file mode 100644 index 0000000..d1c8582 --- /dev/null +++ b/includes/assets/kanban-board.js @@ -0,0 +1,65 @@ +jQuery(document).ready(function($) { + const board = $('#kanban-board'); + if (board.length === 0) { + return; + } + + // Função para inicializar o 'sortable' (arrastar e soltar) + $('.kanban-column-body').sortable({ + connectWith: ".kanban-column-body", // Permite arrastar entre colunas + placeholder: "kanban-card-placeholder", // Estilo do espaço reservado + opacity: 0.8, // Deixa o card semitransparente ao arrastar + revert: 200, // Animação suave ao soltar + start: function(event, ui) { + // Garante que o placeholder tenha a mesma altura do card arrastado + ui.placeholder.height(ui.item.outerHeight()); + }, + // Função chamada quando um card é solto em uma nova coluna + receive: function(event, ui) { + const postId = ui.item.data('post-id'); + const newStatus = $(this).data('status-slug'); + const originalFooterHTML = ui.item.find('.kanban-card-footer').html(); // Salva o estado original + + // --- ATUALIZAÇÃO OTIMISTA --- + // 1. Atualiza a UI imediatamente para parecer instantâneo. + // Adiciona uma classe para um feedback visual sutil. + ui.item.addClass('kanban-card-saving'); + + // 2. Envia a atualização para o WordPress em segundo plano. + $.ajax({ + url: kanban_ajax.ajax_url, + type: 'POST', + data: { + action: 'update_demand_status', // Ação do WordPress + nonce: kanban_ajax.nonce, + post_id: postId, + new_status: newStatus + }, + success: function(response) { + if (response.success) { + // Sucesso! A UI já está correta. Apenas damos um feedback de sucesso. + ui.item.addClass('kanban-card-success'); + setTimeout(function() { + ui.item.removeClass('kanban-card-success'); + }, 1500); + } else { + // Erro! Desfaz a alteração na UI e avisa o usuário. + $(ui.sender).sortable('cancel'); + ui.item.find('.kanban-card-footer').html(originalFooterHTML); // Restaura o rodapé + alert('Erro ao atualizar o status: ' + (response.data.message || 'Tente novamente.')); + } + }, + error: function() { + // Erro de conexão! Também desfaz a alteração. + $(ui.sender).sortable('cancel'); + ui.item.find('.kanban-card-footer').html(originalFooterHTML); // Restaura o rodapé + alert('Erro de comunicação. Tente novamente.'); + }, + complete: function() { + // Independentemente do resultado, remove a classe de "salvando". + ui.item.removeClass('kanban-card-saving'); + } + }); + } + }).disableSelection(); +}); \ No newline at end of file diff --git a/includes/assets/kanban-style.css b/includes/assets/kanban-style.css new file mode 100644 index 0000000..3c07c6d --- /dev/null +++ b/includes/assets/kanban-style.css @@ -0,0 +1,160 @@ +/* Estilos Gerais do Quadro Kanban */ +.wrap h1 { + margin-bottom: 20px; + font-size: 24px; + font-weight: 600; +} + +#kanban-board { + display: flex; + gap: 16px; + overflow-x: auto; + padding-bottom: 16px; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; +} + +/* Colunas do Kanban */ +.kanban-column { + flex: 1; + min-width: 300px; + max-width: 320px; + background-color: #f0f2f5; /* Um cinza mais suave */ + border-radius: 12px; + display: flex; + flex-direction: column; +} + +.kanban-column-header { + display: flex; + justify-content: space-between; + align-items: center; + font-size: 15px; + font-weight: 600; + padding: 12px 16px; + border-bottom: 1px solid #e5e7eb; + color: #1f2937; +} + +.kanban-card-count { + background-color: #e5e7eb; + color: #4b5563; + font-size: 12px; + font-weight: 600; + padding: 2px 8px; + border-radius: 12px; +} + +.kanban-column-body { + padding: 8px; + flex-grow: 1; + min-height: 500px; + transition: background-color 0.2s ease; +} + +/* Cards de Demanda */ +.kanban-card { + background-color: #ffffff; + border-radius: 8px; + box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05); + border: 1px solid #e5e7eb; + padding: 16px; + margin-bottom: 12px; + cursor: grab; + border-left-width: 4px; + transition: box-shadow 0.2s ease, transform 0.2s ease; +} + +.kanban-card:hover { + box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -2px rgba(0, 0, 0, 0.1); + transform: translateY(-2px); +} + +.kanban-card:active { + cursor: grabbing; + background-color: #f9fafb; + box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -4px rgba(0, 0, 0, 0.1); +} + +.kanban-card-title { + font-weight: 600; + margin-bottom: 12px; + font-size: 14px; + line-height: 1.4; +} + +.kanban-card-title a { + text-decoration: none; + color: #111827; +} + +.kanban-card-title a:hover { + color: #4f46e5; /* Indigo */ +} + +.kanban-card-meta { + display: flex; + flex-direction: column; + gap: 8px; + font-size: 13px; + color: #6b7280; + margin-bottom: 16px; +} + +.kanban-card-meta .meta-item { + display: flex; + align-items: center; + gap: 6px; +} + +.kanban-card-meta .dashicons { + font-size: 18px; + color: #9ca3af; + height: 18px; + width: 18px; +} + +.kanban-card-footer { + display: flex; +} + +.kanban-priority-badge { + padding: 3px 10px; + border-radius: 16px; + font-weight: 500; + font-size: 12px; + color: #fff; +} + +/* Cores para Prioridades */ +.priority-high { border-left-color: #ef4444; } +.priority-high .kanban-priority-badge { background-color: #ef4444; } /* Vermelho */ + +.priority-medium-high { border-left-color: #f97316; } +.priority-medium-high .kanban-priority-badge { background-color: #f97316; } /* Laranja */ + +.priority-medium { border-left-color: #3b82f6; } +.priority-medium .kanban-priority-badge { background-color: #3b82f6; } /* Azul */ + +.low, .priority-low { border-left-color: #6b7280; } +.low .kanban-priority-badge, .priority-low .kanban-priority-badge { background-color: #6b7280; } /* Cinza */ + +/* Placeholder para arrastar e soltar */ +.kanban-card-placeholder { + background-color: #e0e7ff; + border: 1px dashed #a5b4fc; + height: 80px; + margin-bottom: 12px; + border-radius: 8px; +} + +/* Feedback de sucesso */ +.kanban-card-success { + transition: background-color 0.5s ease; + background-color: #f0fdf4 !important; /* Verde muito claro */ + border-color: #bbf7d0; +} + +.saving-feedback { + font-style: italic; + color: #64748b; +} \ No newline at end of file diff --git a/includes/assets/script.js b/includes/assets/script.js index c90762c..0205a14 100644 --- a/includes/assets/script.js +++ b/includes/assets/script.js @@ -1,53 +1,20 @@ jQuery(document).ready(function($) { var $form = $('#sistema-arte-form'); if ($form.length) { - // Definir data padrão (hoje + 7 dias) no carregamento - var today = new Date(); - today.setDate(today.getDate() + 7); - today.setHours(17, 0, 0, 0); // Define para 17:00 - - // Formata para o formato esperado pelo input datetime-local (YYYY-MM-DDTHH:MM) - var formattedDate = today.toISOString().slice(0, 16); - - // Define o valor do campo se ele estiver vazio - if (!$('#due_date').val()) { - $('#due_date').val(formattedDate); - } - $form.on('submit', function(e) { - // Coleta todos os dados dos novos campos - var fullName = $('#full_name').val() || ''; - var department = $('#department').val() || ''; - var phone = $('#phone').val() || ''; - var additionalInfo = $('#additional_info').val() || ''; - - // Formata a descrição com HTML - var formattedDescription = - "
" + additionalInfo.replace(//g, '>') + "
" + - "