Initial commit
This commit is contained in:
2
.gitattributes
vendored
Normal file
2
.gitattributes
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
# Auto detect text files and perform LF normalization
|
||||||
|
* text=auto
|
59
README.md
Normal file
59
README.md
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
# Sistema Arte
|
||||||
|
|
||||||
|
Plugin WordPress para gerenciamento de tarefas integrado à API Vikunja.
|
||||||
|
|
||||||
|
## 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.
|
||||||
|
|
||||||
|
## 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
|
||||||
|
|
||||||
|
## 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
|
||||||
|
|
||||||
|
## 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
|
||||||
|
|
||||||
|
## Dependências
|
||||||
|
- [Tailwind CSS](https://tailwindcss.com/) (via CDN)
|
||||||
|
- jQuery (WordPress padrão)
|
||||||
|
|
||||||
|
## 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.
|
98
includes/api.php
Normal file
98
includes/api.php
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
<?php
|
||||||
|
// Função genérica para fazer requisições à API
|
||||||
|
function makeApiRequest($url, $token, $method = 'GET', $data = null) {
|
||||||
|
$args = [
|
||||||
|
'headers' => [
|
||||||
|
'Authorization' => 'Bearer ' . sanitize_text_field($token),
|
||||||
|
'Content-Type' => 'application/json',
|
||||||
|
],
|
||||||
|
'method' => $method,
|
||||||
|
'timeout' => 30,
|
||||||
|
];
|
||||||
|
|
||||||
|
if ($data && in_array($method, ['PUT', 'POST'])) {
|
||||||
|
$args['body'] = wp_json_encode($data);
|
||||||
|
if ($args['body'] === false) {
|
||||||
|
error_log('Sistema Arte: Falha ao codificar JSON para a requisição API');
|
||||||
|
return [
|
||||||
|
'success' => false,
|
||||||
|
'error' => 'Erro interno: Falha ao codificar dados da requisição.',
|
||||||
|
'http_code' => 0
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$response = wp_remote_request($url, $args);
|
||||||
|
|
||||||
|
if (is_wp_error($response)) {
|
||||||
|
$error_message = $response->get_error_message();
|
||||||
|
error_log('Sistema Arte: Erro na requisição API: ' . $error_message);
|
||||||
|
return [
|
||||||
|
'success' => false,
|
||||||
|
'error' => 'Erro na requisição: ' . esc_html($error_message),
|
||||||
|
'http_code' => 0
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
$http_code = wp_remote_retrieve_response_code($response);
|
||||||
|
$body = wp_remote_retrieve_body($response);
|
||||||
|
|
||||||
|
$result = json_decode($body, true);
|
||||||
|
if (json_last_error() !== JSON_ERROR_NONE) {
|
||||||
|
error_log('Sistema Arte: Erro ao decodificar JSON: ' . json_last_error_msg());
|
||||||
|
return [
|
||||||
|
'success' => false,
|
||||||
|
'error' => 'Erro ao processar resposta da API.',
|
||||||
|
'http_code' => $http_code,
|
||||||
|
'raw_response' => $body
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($http_code >= 200 && $http_code < 300) {
|
||||||
|
return ['success' => true, 'data' => $result, 'http_code' => $http_code];
|
||||||
|
} else {
|
||||||
|
$error = isset($result['message']) ? $result['message'] : $body;
|
||||||
|
error_log('Sistema Arte: Erro na API (HTTP ' . $http_code . '): ' . $error);
|
||||||
|
return [
|
||||||
|
'success' => false,
|
||||||
|
'error' => esc_html($error),
|
||||||
|
'http_code' => $http_code,
|
||||||
|
'raw_response' => $body
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Função para listar tarefas do projeto
|
||||||
|
function listTasks($apiBase, $token, $projectId) {
|
||||||
|
$url = rtrim($apiBase, '/') . '/projects/' . absint($projectId) . '/tasks';
|
||||||
|
$response = makeApiRequest($url, $token);
|
||||||
|
|
||||||
|
if ($response['success']) {
|
||||||
|
return $response['data'];
|
||||||
|
} else {
|
||||||
|
error_log('Sistema Arte: Erro ao listar tarefas: ' . $response['error']);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Função para adicionar tarefa
|
||||||
|
function addTask($apiBase, $token, $task, $projectId) {
|
||||||
|
// Validação básica
|
||||||
|
if (empty($task['title']) || empty($task['project_id'])) {
|
||||||
|
error_log('Sistema Arte: Título ou project_id ausente na tarefa.');
|
||||||
|
return ['success' => false, 'error' => 'Título e project_id são obrigatórios.'];
|
||||||
|
}
|
||||||
|
if ($task['project_id'] !== $projectId) {
|
||||||
|
error_log('Sistema Arte: project_id mismatch: ' . $task['project_id'] . ' != ' . $projectId);
|
||||||
|
return [
|
||||||
|
'success' => false,
|
||||||
|
'error' => "project_id no corpo ({$task['project_id']}) deve corresponder ao ID do projeto no caminho ({$projectId})."
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
$url = rtrim($apiBase, '/') . '/projects/' . absint($projectId) . '/tasks';
|
||||||
|
$response = makeApiRequest($url, $token, 'PUT', $task);
|
||||||
|
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
?>
|
56
includes/assets/script.js
Normal file
56
includes/assets/script.js
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
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 =
|
||||||
|
"<div style='font-family: Arial, sans-serif;'>" +
|
||||||
|
"<h3 style='color: #2563eb; border-bottom: 1px solid #ddd; padding-bottom: 5px;'>SOLICITAÇÃO</h3>" +
|
||||||
|
"<ul style='list-style-type: none; padding-left: 0;'>" +
|
||||||
|
"<li><strong>Solicitante:</strong> " + fullName.replace(/</g, '<').replace(/>/g, '>') + "</li>" +
|
||||||
|
"<li><strong>Secretaria:</strong> " + department.replace(/</g, '<').replace(/>/g, '>') + "</li>" +
|
||||||
|
"<li><strong>Contato:</strong> " + phone.replace(/</g, '<').replace(/>/g, '>') + "</li>" +
|
||||||
|
"</ul>" +
|
||||||
|
"<h3 style='color: #2563eb; border-bottom: 1px solid #ddd; padding-bottom: 5px;'>DETALHES</h3>" +
|
||||||
|
"<p style='white-space: pre-line;'>" + additionalInfo.replace(/</g, '<').replace(/>/g, '>') + "</p>" +
|
||||||
|
"</div>";
|
||||||
|
|
||||||
|
// Atribui ao campo de descrição que será enviado ao backend
|
||||||
|
$('#description').val(formattedDescription);
|
||||||
|
|
||||||
|
// Define valores padrão para os campos originais se necessário
|
||||||
|
if (!$('#due_date').val()) {
|
||||||
|
var today = new Date();
|
||||||
|
today.setDate(today.getDate() + 7);
|
||||||
|
today.setHours(17, 0);
|
||||||
|
$('#due_date').val(today.toISOString().slice(0, 16));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$('#priority').val()) {
|
||||||
|
$('#priority').val(3); // Prioridade média por padrão
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
console.warn('Sistema Arte: Formulário #sistema-arte-form não encontrado.');
|
||||||
|
}
|
||||||
|
});
|
6
includes/config.php
Normal file
6
includes/config.php
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?php
|
||||||
|
// Configurações da API
|
||||||
|
$apiBase = 'http://192.168.0.54:8002/api/v1';
|
||||||
|
$token = 'tk_6bb6ebf73e487c79c76e9d6546daa7595563b41f';
|
||||||
|
$projectId = 1; // Confirmado como válido (Inbox)
|
||||||
|
?>
|
128
includes/templates/form.php
Normal file
128
includes/templates/form.php
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
<?php
|
||||||
|
function sistema_arte_form_template($message) {
|
||||||
|
// Calcular data padrão (hoje + 7 dias) no formato YYYY-MM-DDTHH:MM
|
||||||
|
$default_due_date = '';
|
||||||
|
try {
|
||||||
|
$date = new DateTime();
|
||||||
|
$date->add(new DateInterval('P7D'));
|
||||||
|
$date->setTime(17, 0); // Define para 17:00
|
||||||
|
$default_due_date = $date->format('Y-m-d\TH:i');
|
||||||
|
} catch (Exception $e) {
|
||||||
|
error_log('Erro ao calcular data padrão: ' . $e->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verificar se já existe um valor POST para manter na reexibição do formulário
|
||||||
|
$due_date_value = isset($_POST['due_date']) ? esc_attr(wp_unslash($_POST['due_date'])) : $default_due_date;
|
||||||
|
?>
|
||||||
|
<!-- Coluna Esquerda - Formulário -->
|
||||||
|
<div class="bg-white rounded-lg shadow-lg p-6">
|
||||||
|
<h2 class="text-xl font-bold text-gray-800 mb-6 border-b pb-2">Adicionar uma nova demanda</h2>
|
||||||
|
|
||||||
|
<?php if ($message): ?>
|
||||||
|
<div class="p-4 mb-6 rounded-lg <?php echo strpos($message, 'sucesso') !== false ? 'bg-green-100 text-green-800' : 'bg-red-100 text-red-800'; ?>">
|
||||||
|
<?php echo wp_kses_post($message); ?>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<form method="POST" class="space-y-4" id="sistema-arte-form">
|
||||||
|
<?php wp_nonce_field('sistema_arte_nonce', 'sistema_arte_nonce'); ?>
|
||||||
|
|
||||||
|
<!-- Campo Título (obrigatório) -->
|
||||||
|
<div>
|
||||||
|
<label for="title" class="block text-sm font-medium text-gray-700 mb-1">Título da Arte*</label>
|
||||||
|
<input type="text" id="title" name="title" required
|
||||||
|
class="mt-1 block w-full p-3 border border-gray-300 rounded-md focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 shadow-sm"
|
||||||
|
value="<?php echo esc_attr(isset($_POST['title']) ? wp_unslash($_POST['title']) : ''); ?>">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Novos campos para informações do usuário -->
|
||||||
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
|
<!-- Nome Completo -->
|
||||||
|
<div>
|
||||||
|
<label for="full_name" class="block text-sm font-medium text-gray-700 mb-1">Seu nome completo *</label>
|
||||||
|
<input type="text" id="full_name" name="full_name" required
|
||||||
|
class="mt-1 block w-full p-3 border border-gray-300 rounded-md focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 shadow-sm"
|
||||||
|
value="<?php echo esc_attr(isset($_POST['full_name']) ? wp_unslash($_POST['full_name']) : ''); ?>">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Secretaria -->
|
||||||
|
<div>
|
||||||
|
<label for="department" class="block text-sm font-medium text-gray-700 mb-1">Secretaria *</label>
|
||||||
|
<select id="department" name="department" required
|
||||||
|
class="mt-1 block w-full p-3 border border-gray-300 rounded-md focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 shadow-sm">
|
||||||
|
<option value="">Selecione...</option>
|
||||||
|
<option value="SARH" <?php echo (isset($_POST['department']) && $_POST['department'] == 'SARH') ? 'selected' : ''; ?>>SARH</option>
|
||||||
|
<option value="SEMAP" <?php echo (isset($_POST['department']) && $_POST['department'] == 'SEMAP') ? 'selected' : ''; ?>>SEMAP</option>
|
||||||
|
<option value="SECOM" <?php echo (isset($_POST['department']) && $_POST['department'] == 'SECOM') ? 'selected' : ''; ?>>SECOM</option>
|
||||||
|
<option value="SEMCI" <?php echo (isset($_POST['department']) && $_POST['department'] == 'SEMCI') ? 'selected' : ''; ?>>SEMCI</option>
|
||||||
|
<option value="SEDESO" <?php echo (isset($_POST['department']) && $_POST['department'] == 'SEDESO') ? 'selected' : ''; ?>>SEDESO</option>
|
||||||
|
<option value="SEDUC" <?php echo (isset($_POST['department']) && $_POST['department'] == 'SEDUC') ? 'selected' : ''; ?>>SEDUC</option>
|
||||||
|
<option value="SEFIN" <?php echo (isset($_POST['department']) && $_POST['department'] == 'SEFIN') ? 'selected' : ''; ?>>SEFIN</option>
|
||||||
|
<option value="SEGOV" <?php echo (isset($_POST['department']) && $_POST['department'] == 'SEGOV') ? 'selected' : ''; ?>>SEGOV</option>
|
||||||
|
<option value="SEMMA" <?php echo (isset($_POST['department']) && $_POST['department'] == 'SEMMA') ? 'selected' : ''; ?>>SEMMA</option>
|
||||||
|
<option value="SEMOSP" <?php echo (isset($_POST['department']) && $_POST['department'] == 'SEMOSP') ? 'selected' : ''; ?>>SEMOSP</option>
|
||||||
|
<option value="SEPLAN" <?php echo (isset($_POST['department']) && $_POST['department'] == 'SEPLAN') ? 'selected' : ''; ?>>SEPLAN</option>
|
||||||
|
<option value="PGM" <?php echo (isset($_POST['department']) && $_POST['department'] == 'PGM') ? 'selected' : ''; ?>>PGM</option>
|
||||||
|
<option value="SEMS" <?php echo (isset($_POST['department']) && $_POST['department'] == 'SEMS') ? 'selected' : ''; ?>>SEMS</option>
|
||||||
|
<option value="SESP" <?php echo (isset($_POST['department']) && $_POST['department'] == 'SESP') ? 'selected' : ''; ?>>SESP</option>
|
||||||
|
<option value="SELTC" <?php echo (isset($_POST['department']) && $_POST['department'] == 'SELTC') ? 'selected' : ''; ?>>SELTC</option>
|
||||||
|
<option value="SEDEC" <?php echo (isset($_POST['department']) && $_POST['department'] == 'SEDEC') ? 'selected' : ''; ?>>SEDEC</option>
|
||||||
|
<option value="SEMOB" <?php echo (isset($_POST['department']) && $_POST['department'] == 'SEMOB') ? 'selected' : ''; ?>>SEMOB</option>
|
||||||
|
<option value="OUTRAS" <?php echo (isset($_POST['department']) && $_POST['department'] == 'OUTRAS') ? 'selected' : ''; ?>>OUTRAS</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Telefone/WhatsApp -->
|
||||||
|
<div>
|
||||||
|
<label for="phone" class="block text-sm font-medium text-gray-700 mb-1">Telefone/WhatsApp *</label>
|
||||||
|
<input type="tel" id="phone" name="phone" required
|
||||||
|
class="mt-1 block w-full p-3 border border-gray-300 rounded-md focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 shadow-sm"
|
||||||
|
value="<?php echo esc_attr(isset($_POST['phone']) ? wp_unslash($_POST['phone']) : ''); ?>"
|
||||||
|
placeholder="(99) 99999-9999">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Descrição (campo original, agora oculto) -->
|
||||||
|
<input type="hidden" id="description" name="description">
|
||||||
|
|
||||||
|
<!-- Campo para detalhes adicionais -->
|
||||||
|
<div>
|
||||||
|
<label for="additional_info" class="block text-sm font-medium text-gray-700 mb-1">Detalhes da Solicitação *</label>
|
||||||
|
<textarea id="additional_info" name="additional_info" rows="4"
|
||||||
|
class="mt-1 block w-full p-3 border border-gray-300 rounded-md focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 shadow-sm"
|
||||||
|
required><?php echo esc_textarea(isset($_POST['additional_info']) ? wp_unslash($_POST['additional_info']) : ''); ?></textarea>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- NOVOS CAMPOS VISÍVEIS: Data de Entrega e Prioridade -->
|
||||||
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
|
<!-- Data de Entrega -->
|
||||||
|
<div>
|
||||||
|
<label for="due_date" class="block text-sm font-medium text-gray-700 mb-1">Data de Entrega</label>
|
||||||
|
<input type="datetime-local" id="due_date" name="due_date"
|
||||||
|
class="mt-1 block w-full p-3 border border-gray-300 rounded-md focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 shadow-sm"
|
||||||
|
value="<?php echo $due_date_value; ?>">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Prioridade -->
|
||||||
|
<div>
|
||||||
|
<label for="priority" class="block text-sm font-medium text-gray-700 mb-1">Prioridade</label>
|
||||||
|
<select id="priority" name="priority"
|
||||||
|
class="mt-1 block w-full p-3 border border-gray-300 rounded-md focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 shadow-sm">
|
||||||
|
<option value="1" <?php echo (isset($_POST['priority']) && $_POST['priority'] == '1') ? 'selected' : ''; ?>>Alta</option>
|
||||||
|
<option value="2" <?php echo (isset($_POST['priority']) && $_POST['priority'] == '2') ? 'selected' : ''; ?>>Média-Alta</option>
|
||||||
|
<option value="3" <?php echo (!isset($_POST['priority']) || $_POST['priority'] == '3') ? 'selected' : ''; ?>>Média</option>
|
||||||
|
<option value="4" <?php echo (isset($_POST['priority']) && $_POST['priority'] == '4') ? 'selected' : ''; ?>>Baixa</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Botão de envio -->
|
||||||
|
<button type="submit"
|
||||||
|
class="w-full p-3 bg-indigo-600 text-white rounded-md hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 transition-colors duration-200 shadow">
|
||||||
|
Enviar Solicitação
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
?>
|
64
includes/templates/tasks.php
Normal file
64
includes/templates/tasks.php
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
<?php
|
||||||
|
function sistema_arte_tasks_template($tasks) {
|
||||||
|
?>
|
||||||
|
<!-- Coluna Direita - Lista de Tarefas -->
|
||||||
|
<div class="bg-white rounded-lg shadow-lg p-6">
|
||||||
|
<h2 class="text-xl font-bold text-gray-800 mb-6 border-b pb-2">Demandas Pendentes</h2>
|
||||||
|
<?php if ($tasks): ?>
|
||||||
|
<div class="overflow-x-auto">
|
||||||
|
<table class="w-full text-left">
|
||||||
|
<thead class="bg-gray-50">
|
||||||
|
<tr>
|
||||||
|
<th class="p-3 border-b font-semibold text-gray-700">ID</th>
|
||||||
|
<th class="p-3 border-b font-semibold text-gray-700">Título</th>
|
||||||
|
<th class="p-3 border-b font-semibold text-gray-700">Vencimento</th>
|
||||||
|
<th class="p-3 border-b font-semibold text-gray-700">Prioridade</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<?php foreach ($tasks as $task): ?>
|
||||||
|
<tr class="border-b hover:bg-gray-50 transition-colors duration-150">
|
||||||
|
<td class="p-3 text-gray-600"><?php echo esc_html($task['id']); ?></td>
|
||||||
|
<td class="p-3 font-medium text-gray-800"><?php echo esc_html($task['title']); ?></td>
|
||||||
|
<td class="p-3 text-gray-600">
|
||||||
|
<?php
|
||||||
|
if ($task['due_date']) {
|
||||||
|
try {
|
||||||
|
$date = new DateTime($task['due_date']);
|
||||||
|
echo esc_html($date->format('d/m/Y H:i'));
|
||||||
|
} catch (Exception $e) {
|
||||||
|
echo '-';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
echo '-';
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
</td>
|
||||||
|
<td class="p-3">
|
||||||
|
<?php if ($task['priority']): ?>
|
||||||
|
<span class="px-2 py-1 rounded-full text-xs font-medium
|
||||||
|
<?php echo esc_attr($task['priority'] <= 2 ? 'bg-red-100 text-red-800' :
|
||||||
|
($task['priority'] <= 3 ? 'bg-yellow-100 text-yellow-800' : 'bg-green-100 text-green-800')); ?>">
|
||||||
|
<?php echo esc_html($task['priority']); ?>
|
||||||
|
</span>
|
||||||
|
<?php else: ?>
|
||||||
|
<span class="px-2 py-1 rounded-full text-xs font-medium bg-gray-100 text-gray-800">-</span>
|
||||||
|
<?php endif; ?>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<?php else: ?>
|
||||||
|
<div class="text-center py-8">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-12 w-12 mx-auto text-gray-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2" />
|
||||||
|
</svg>
|
||||||
|
<p class="mt-4 text-gray-600">Nenhuma tarefa encontrada ou erro ao carregar.</p>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
?>
|
160
sistema-arte.php
Normal file
160
sistema-arte.php
Normal file
@@ -0,0 +1,160 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Plugin Name: Sistema Arte
|
||||||
|
* Description: Plugin de gerenciamento de tarefas integrado à API Vikunja.
|
||||||
|
* Shortcode disponível: [Sistema-Arte]
|
||||||
|
* Version: 1.0
|
||||||
|
* Author: Marco Antonio Vivas
|
||||||
|
* Text Domain: sistema-arte
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (!defined('ABSPATH')) {
|
||||||
|
exit; // Exit if accessed directly
|
||||||
|
}
|
||||||
|
|
||||||
|
// Define plugin path
|
||||||
|
define('SISTEMA_ARTE_PATH', plugin_dir_path(__FILE__));
|
||||||
|
|
||||||
|
// Verify and include necessary files
|
||||||
|
$required_files = [
|
||||||
|
SISTEMA_ARTE_PATH . 'includes/config.php',
|
||||||
|
SISTEMA_ARTE_PATH . 'includes/api.php',
|
||||||
|
SISTEMA_ARTE_PATH . 'includes/templates/form.php',
|
||||||
|
SISTEMA_ARTE_PATH . 'includes/templates/tasks.php'
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($required_files as $file) {
|
||||||
|
if (file_exists($file)) {
|
||||||
|
require_once $file;
|
||||||
|
} else {
|
||||||
|
wp_die('Erro fatal: Arquivo necessário não encontrado - ' . esc_html($file));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enqueue styles and scripts
|
||||||
|
function sistema_arte_enqueue_assets() {
|
||||||
|
// Enqueue Tailwind CSS via CDN
|
||||||
|
wp_enqueue_style('sistema-arte-tailwind', 'https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css', [], '2.2.19');
|
||||||
|
|
||||||
|
// Enqueue custom script
|
||||||
|
wp_enqueue_script('sistema-arte-script', plugins_url('/includes/assets/script.js', __FILE__), ['jquery'], '1.0.1', true);
|
||||||
|
|
||||||
|
// Localize script for form validation
|
||||||
|
wp_localize_script('sistema-arte-script', 'sistemaArte', [
|
||||||
|
'ajax_url' => admin_url('admin-ajax.php'),
|
||||||
|
'nonce' => wp_create_nonce('sistema_arte_nonce')
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
add_action('wp_enqueue_scripts', 'sistema_arte_enqueue_assets');
|
||||||
|
|
||||||
|
// Register shortcode
|
||||||
|
function sistema_arte_shortcode($atts) {
|
||||||
|
// Start output buffering
|
||||||
|
ob_start();
|
||||||
|
|
||||||
|
// Load config variables
|
||||||
|
global $apiBase, $token, $projectId;
|
||||||
|
|
||||||
|
// Process form submission
|
||||||
|
$message = '';
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['sistema_arte_nonce']) && wp_verify_nonce($_POST['sistema_arte_nonce'], 'sistema_arte_nonce')) {
|
||||||
|
$title = isset($_POST['title']) ? sanitize_text_field(trim($_POST['title'])) : '';
|
||||||
|
$full_name = isset($_POST['full_name']) ? sanitize_text_field(trim($_POST['full_name'])) : '';
|
||||||
|
$department = isset($_POST['department']) ? sanitize_text_field(trim($_POST['department'])) : '';
|
||||||
|
$phone = isset($_POST['phone']) ? sanitize_text_field(trim($_POST['phone'])) : '';
|
||||||
|
$additional_info = isset($_POST['additional_info']) ? sanitize_textarea_field(trim($_POST['additional_info'])) : '';
|
||||||
|
$due_date = isset($_POST['due_date']) ? sanitize_text_field(trim($_POST['due_date'])) : '';
|
||||||
|
$priority = isset($_POST['priority']) ? absint($_POST['priority']) : null;
|
||||||
|
|
||||||
|
// Validações
|
||||||
|
$errors = [];
|
||||||
|
if (empty($title)) {
|
||||||
|
$errors[] = 'O título é obrigatório.';
|
||||||
|
}
|
||||||
|
if (empty($full_name)) {
|
||||||
|
$errors[] = 'O nome completo é obrigatório.';
|
||||||
|
}
|
||||||
|
if (empty($department)) {
|
||||||
|
$errors[] = 'A secretaria é obrigatória.';
|
||||||
|
}
|
||||||
|
if (empty($phone)) {
|
||||||
|
$errors[] = 'O telefone/WhatsApp é obrigatório.';
|
||||||
|
}
|
||||||
|
if (empty($additional_info)) {
|
||||||
|
$errors[] = 'Os detalhes da solicitação são obrigatórios.';
|
||||||
|
}
|
||||||
|
if ($due_date) {
|
||||||
|
try {
|
||||||
|
$date = new DateTime($due_date);
|
||||||
|
$due_date = $date->format('c'); // Formato ISO 8601
|
||||||
|
} catch (Exception $e) {
|
||||||
|
$errors[] = 'Data de vencimento inválida.';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($priority !== null && ($priority < 1 || $priority > 5)) {
|
||||||
|
$errors[] = 'Prioridade deve ser entre 1 e 5.';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($errors)) {
|
||||||
|
// Format description
|
||||||
|
$description = "<div style='font-family: Arial, sans-serif;'>" .
|
||||||
|
"<h3 style='color: #2563eb; border-bottom: 1px solid #ddd; padding-bottom: 5px;'>SOLICITAÇÃO</h3>" .
|
||||||
|
"<ul style='list-style-type: none; padding-left: 0;'>" .
|
||||||
|
"<li><strong>Solicitante:</strong> " . esc_html($full_name) . "</li>" .
|
||||||
|
"<li><strong>Secretaria:</strong> " . esc_html($department) . "</li>" .
|
||||||
|
"<li><strong>Contato:</strong> " . esc_html($phone) . "</li>" .
|
||||||
|
"</ul>" .
|
||||||
|
"<h3 style='color: #2563eb; border-bottom: 1px solid #ddd; padding-bottom: 5px;'>DETALHES</h3>" .
|
||||||
|
"<p style='white-space: pre-line;'>" . esc_html($additional_info) . "</p>" .
|
||||||
|
"</div>";
|
||||||
|
|
||||||
|
$task = [
|
||||||
|
'title' => $title,
|
||||||
|
'project_id' => $projectId,
|
||||||
|
'description' => $description,
|
||||||
|
];
|
||||||
|
if ($due_date) {
|
||||||
|
$task['due_date'] = $due_date;
|
||||||
|
}
|
||||||
|
if ($priority !== null) {
|
||||||
|
$task['priority'] = $priority;
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = addTask($apiBase, $token, $task, $projectId);
|
||||||
|
if ($result['success']) {
|
||||||
|
$message = "Tarefa criada com sucesso! ID: {$result['data']['id']}";
|
||||||
|
} else {
|
||||||
|
$message = "Erro ao criar tarefa: {$result['error']} (HTTP {$result['http_code']})";
|
||||||
|
if ($result['http_code'] == 403) {
|
||||||
|
$message .= "<br>Permissão negada. Verifique se o usuário do token tem acesso de escrita no projeto.";
|
||||||
|
} elseif ($result['http_code'] == 400) {
|
||||||
|
$message .= "<br>Dados inválidos. Verifique o formato dos campos da tarefa.";
|
||||||
|
} elseif ($result['http_code'] == 404) {
|
||||||
|
$message .= "<br>Projeto não encontrado. Confirme se o project_id é válido.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$message = 'Erros no formulário:<ul><li>' . implode('</li><li>', array_map('esc_html', $errors)) . '</li></ul>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// List tasks
|
||||||
|
$tasks = listTasks($apiBase, $token, $projectId);
|
||||||
|
|
||||||
|
// Load templates
|
||||||
|
?>
|
||||||
|
<div class="container mx-auto px-4 py-8"></br>
|
||||||
|
<h1 class="text-3xl font-bold text-gray-800 mb-8 text-center">Sistema de gerenciamento de artes</h1>
|
||||||
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||||
|
<?php
|
||||||
|
sistema_arte_form_template($message);
|
||||||
|
sistema_arte_tasks_template($tasks);
|
||||||
|
?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<?php
|
||||||
|
|
||||||
|
return ob_get_clean();
|
||||||
|
}
|
||||||
|
add_shortcode('Sistema-Arte', 'sistema_arte_shortcode');
|
||||||
|
?>
|
Reference in New Issue
Block a user