Vikunja para Wordpress

This commit is contained in:
2025-09-20 19:19:00 -03:00
parent 65e3309266
commit a7cbbb7b47
7 changed files with 764 additions and 140 deletions

View File

@@ -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();
});

View File

@@ -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;
}

View File

@@ -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 =
"<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, '&lt;').replace(/>/g, '&gt;') + "</li>" +
"<li><strong>Secretaria:</strong> " + department.replace(/</g, '&lt;').replace(/>/g, '&gt;') + "</li>" +
"<li><strong>Contato:</strong> " + phone.replace(/</g, '&lt;').replace(/>/g, '&gt;') + "</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, '&lt;').replace(/>/g, '&gt;') + "</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
}
// Adiciona máscara ao campo de telefone
var phoneInput = $('#phone');
if (phoneInput.length && typeof phoneInput.mask === 'function') {
var maskBehavior = function (val) {
return val.replace(/\D/g, '').length === 11 ? '(00) 00000-0000' : '(00) 0000-00009';
},
options = {onKeyPress: function(val, e, field, options) {
field.mask(maskBehavior.apply({}, arguments), options);
}
};
phoneInput.mask(maskBehavior, options);
}
$form.on('submit', function() {
return true;
});
} else {