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
|
40
assets/css/admin.css
Normal file
40
assets/css/admin.css
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
/* Estilos para a lista de tickets no painel de administração */
|
||||||
|
|
||||||
|
/* Ajuste de largura das colunas */
|
||||||
|
.post-type-ticket .wp-list-table #ticket_id {
|
||||||
|
width: 8%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-type-ticket .wp-list-table #status {
|
||||||
|
width: 12%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-type-ticket .wp-list-table #ticket_type {
|
||||||
|
width: 15%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Estilo base para as etiquetas de status */
|
||||||
|
.sts-admin-status {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 4px 12px;
|
||||||
|
border-radius: 15px;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #fff;
|
||||||
|
text-shadow: none;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Cores das etiquetas de status */
|
||||||
|
.sts-admin-status.status-aberto {
|
||||||
|
background-color: #ffb700; /* Amarelo */
|
||||||
|
color: #212529; /* Preto */
|
||||||
|
}
|
||||||
|
|
||||||
|
.sts-admin-status.status-em-andamento {
|
||||||
|
background-color: #0582ca; /* Azul Claro */
|
||||||
|
}
|
||||||
|
|
||||||
|
.sts-admin-status.status-resolvido {
|
||||||
|
background-color: #028c6a; /* Verde Sucesso */
|
||||||
|
}
|
345
assets/css/frontend.css
Normal file
345
assets/css/frontend.css
Normal file
@@ -0,0 +1,345 @@
|
|||||||
|
/* Importando as variáveis de referência */
|
||||||
|
:root {
|
||||||
|
--azul-principal: #006494;
|
||||||
|
--azul-escuro: #003554;
|
||||||
|
--azul-claro: #0582ca;
|
||||||
|
--amarelo: #ffb700;
|
||||||
|
--cinza-claro: #f8f9fa;
|
||||||
|
--cinza: #e9ecef;
|
||||||
|
--preto: #212529;
|
||||||
|
--branco: #ffffff;
|
||||||
|
--verde-sucesso: #028c6a;
|
||||||
|
--vermelho-erro: #a94442;
|
||||||
|
--sombra: 0 4px 6px rgba(0, 0, 0, 0.1);
|
||||||
|
--borda-radius: 8px;
|
||||||
|
--transicao: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Estilo do Formulário de Ticket */
|
||||||
|
.sts-ticket-form {
|
||||||
|
background: var(--branco);
|
||||||
|
padding: 30px;
|
||||||
|
border-radius: var(--borda-radius);
|
||||||
|
box-shadow: var(--sombra);
|
||||||
|
max-width: 700px;
|
||||||
|
margin: 20px auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sts-ticket-form p {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sts-ticket-form label {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--azul-escuro);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sts-ticket-form input[type="text"],
|
||||||
|
.sts-ticket-form textarea,
|
||||||
|
.sts-ticket-form select {
|
||||||
|
width: 100%;
|
||||||
|
padding: 12px;
|
||||||
|
border: 1px solid var(--cinza);
|
||||||
|
border-radius: var(--borda-radius);
|
||||||
|
transition: var(--transicao);
|
||||||
|
background-color: var(--cinza-claro);
|
||||||
|
color: var(--preto);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sts-ticket-form input[type="text"]:focus,
|
||||||
|
.sts-ticket-form textarea:focus,
|
||||||
|
.sts-ticket-form select:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: var(--azul-principal);
|
||||||
|
box-shadow: 0 0 0 3px rgba(0, 100, 148, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sts-ticket-form small {
|
||||||
|
display: block;
|
||||||
|
margin-top: 5px;
|
||||||
|
color: #777;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Botão de Envio */
|
||||||
|
.sts-ticket-form input[type="submit"] {
|
||||||
|
background: var(--azul-principal);
|
||||||
|
color: var(--branco);
|
||||||
|
padding: 12px 25px;
|
||||||
|
border-radius: var(--borda-radius);
|
||||||
|
font-weight: 600;
|
||||||
|
text-decoration: none;
|
||||||
|
transition: var(--transicao);
|
||||||
|
display: inline-block;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sts-ticket-form input[type="submit"]:hover {
|
||||||
|
background: var(--azul-escuro);
|
||||||
|
box-shadow: var(--sombra);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Alertas de Sucesso/Erro */
|
||||||
|
.sts-alert {
|
||||||
|
padding: 15px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
border-radius: var(--borda-radius);
|
||||||
|
max-width: 700px;
|
||||||
|
margin: 20px auto;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sts-alert-success {
|
||||||
|
background-color: #e6f7ee;
|
||||||
|
border-left: 5px solid var(--verde-sucesso);
|
||||||
|
color: var(--verde-sucesso);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sts-alert-error {
|
||||||
|
background-color: #f2dede;
|
||||||
|
border-left: 5px solid var(--vermelho-erro);
|
||||||
|
color: var(--vermelho-erro);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Estilo customizado para o input de arquivo */
|
||||||
|
.sts-file-input-wrapper {
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
display: inline-block;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sts-file-input-wrapper input[type=file] {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
opacity: 0;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sts-file-input-button {
|
||||||
|
background: var(--cinza);
|
||||||
|
color: var(--preto);
|
||||||
|
padding: 10px 15px;
|
||||||
|
border-radius: var(--borda-radius);
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sts-file-name {
|
||||||
|
margin-left: 10px;
|
||||||
|
font-style: italic;
|
||||||
|
color: #555;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Estilo da Lista de Tickets (Meus Tickets) */
|
||||||
|
.sts-section-title {
|
||||||
|
color: var(--azul-escuro);
|
||||||
|
margin-bottom: 25px;
|
||||||
|
font-size: 1.8rem;
|
||||||
|
border-bottom: 2px solid var(--cinza);
|
||||||
|
padding-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sts-ticket-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
||||||
|
gap: 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sts-ticket-card {
|
||||||
|
background: var(--branco);
|
||||||
|
border-radius: var(--borda-radius);
|
||||||
|
box-shadow: var(--sombra);
|
||||||
|
padding: 20px;
|
||||||
|
text-decoration: none;
|
||||||
|
color: var(--preto);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-between;
|
||||||
|
transition: var(--transicao);
|
||||||
|
position: relative;
|
||||||
|
border-left: 5px solid var(--cinza);
|
||||||
|
animation: fadeIn 0.5s ease-out forwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sts-ticket-card:hover {
|
||||||
|
transform: translateY(-5px);
|
||||||
|
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sts-card-ticket-id {
|
||||||
|
position: absolute;
|
||||||
|
top: -10px;
|
||||||
|
left: -10px;
|
||||||
|
background: var(--azul-escuro);
|
||||||
|
color: var(--branco);
|
||||||
|
padding: 5px 10px;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
font-weight: bold;
|
||||||
|
border-radius: var(--borda-radius);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sts-card-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: flex-start;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sts-card-title {
|
||||||
|
font-size: 1.2rem;
|
||||||
|
color: var(--azul-escuro);
|
||||||
|
margin: 0;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sts-ticket-status {
|
||||||
|
font-size: 0.8rem;
|
||||||
|
font-weight: 700;
|
||||||
|
padding: 4px 10px;
|
||||||
|
border-radius: 15px;
|
||||||
|
color: var(--branco);
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sts-card-body p {
|
||||||
|
margin: 0 0 10px;
|
||||||
|
color: #555;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sts-card-footer {
|
||||||
|
margin-top: auto;
|
||||||
|
padding-top: 15px;
|
||||||
|
border-top: 1px solid var(--cinza);
|
||||||
|
font-size: 0.85rem;
|
||||||
|
color: #777;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Cores dos Status */
|
||||||
|
/* Aberto: Laranja */
|
||||||
|
.sts-ticket-card.aberto { border-left-color: var(--amarelo); }
|
||||||
|
.sts-ticket-status.aberto { background-color: var(--amarelo); color: var(--preto); }
|
||||||
|
|
||||||
|
/* Em Andamento: Azul */
|
||||||
|
.sts-ticket-card.em-andamento { border-left-color: var(--azul-claro); }
|
||||||
|
.sts-ticket-status.em-andamento { background-color: var(--azul-claro); color: var(--branco); }
|
||||||
|
|
||||||
|
/* Resolvido: Verde */
|
||||||
|
.sts-ticket-card.resolvido { border-left-color: var(--verde-sucesso); }
|
||||||
|
.sts-ticket-status.resolvido { background-color: var(--verde-sucesso); color: var(--branco); }
|
||||||
|
|
||||||
|
@keyframes fadeIn {
|
||||||
|
from { opacity: 0; transform: translateY(20px); }
|
||||||
|
to { opacity: 1; transform: translateY(0); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Estilos para a página de um único ticket (single-ticket.php) */
|
||||||
|
.sts-content-area {
|
||||||
|
max-width: 900px;
|
||||||
|
margin: 20px auto;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sts-single-ticket {
|
||||||
|
background: var(--branco);
|
||||||
|
padding: 30px;
|
||||||
|
border-radius: var(--borda-radius);
|
||||||
|
box-shadow: var(--sombra);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sts-title-wrapper {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sts-single-ticket-id {
|
||||||
|
background: var(--cinza);
|
||||||
|
padding: 5px 15px;
|
||||||
|
border-radius: 20px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: var(--azul-escuro);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sts-ticket-header .sts-ticket-title {
|
||||||
|
font-size: 2rem;
|
||||||
|
color: var(--azul-escuro);
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sts-ticket-meta {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: center;
|
||||||
|
gap: 15px;
|
||||||
|
padding-bottom: 20px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
border-bottom: 1px solid var(--cinza);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sts-ticket-meta .sts-ticket-status {
|
||||||
|
order: -1; /* Coloca o status primeiro */
|
||||||
|
}
|
||||||
|
|
||||||
|
.sts-ticket-content,
|
||||||
|
.sts-ticket-attachments {
|
||||||
|
margin-bottom: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sts-ticket-content h3,
|
||||||
|
.sts-ticket-attachments h3 {
|
||||||
|
color: var(--azul-escuro);
|
||||||
|
border-bottom: 2px solid var(--cinza);
|
||||||
|
padding-bottom: 5px;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Estilos para a seção de respostas (comentários do WordPress) */
|
||||||
|
.sts-ticket-responses .comments-title {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
color: var(--azul-escuro);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sts-ticket-responses .comment-list {
|
||||||
|
list-style: none;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sts-ticket-responses .comment-body {
|
||||||
|
background: var(--cinza-claro);
|
||||||
|
padding: 20px;
|
||||||
|
border-radius: var(--borda-radius);
|
||||||
|
margin-bottom: 20px;
|
||||||
|
border-left: 4px solid var(--azul-principal);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sts-ticket-responses .comment-meta {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sts-ticket-responses .comment-form-comment textarea {
|
||||||
|
width: 100%;
|
||||||
|
padding: 12px;
|
||||||
|
border: 1px solid var(--cinza);
|
||||||
|
border-radius: var(--borda-radius);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sts-ticket-responses .form-submit .submit {
|
||||||
|
background: var(--azul-principal);
|
||||||
|
color: var(--branco);
|
||||||
|
padding: 10px 20px;
|
||||||
|
border-radius: var(--borda-radius);
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: var(--transicao);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sts-ticket-responses .form-submit .submit:hover {
|
||||||
|
background: var(--azul-escuro);
|
||||||
|
}
|
152
assets/css/style.css
Normal file
152
assets/css/style.css
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
/* Variáveis CSS Globais */
|
||||||
|
:root {
|
||||||
|
--azul-principal: #006494;
|
||||||
|
--azul-escuro: #003554;
|
||||||
|
--azul-claro: #0582ca;
|
||||||
|
--azul-brilhante: #00a6fb;
|
||||||
|
--amarelo: #ffb700;
|
||||||
|
--cinza-claro: #f8f9fa;
|
||||||
|
--cinza: #e9ecef;
|
||||||
|
--preto: #212529;
|
||||||
|
--branco: #ffffff;
|
||||||
|
--sombra: 0 4px 6px rgba(0, 0, 0, 0.1);
|
||||||
|
--borda-radius: 8px;
|
||||||
|
--transicao: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tipografia Base */
|
||||||
|
body {
|
||||||
|
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
|
||||||
|
color: var(--preto);
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Layout Base */
|
||||||
|
.grid-layout {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 250px 1fr;
|
||||||
|
gap: 30px;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Botões e Links */
|
||||||
|
.btn-primario {
|
||||||
|
background: var(--branco);
|
||||||
|
color: var(--azul-principal);
|
||||||
|
padding: 12px 25px;
|
||||||
|
border-radius: var(--borda-radius);
|
||||||
|
font-weight: 600;
|
||||||
|
text-decoration: none;
|
||||||
|
transition: var(--transicao);
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primario:hover {
|
||||||
|
background: var(--azul-principal);
|
||||||
|
color: var(--branco);
|
||||||
|
box-shadow: var(--sombra);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Cards */
|
||||||
|
.ticket-card {
|
||||||
|
background: var(--branco);
|
||||||
|
border-radius: var(--borda-radius);
|
||||||
|
padding: 25px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
box-shadow: var(--sombra);
|
||||||
|
transition: var(--transicao);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ticket-card:hover {
|
||||||
|
transform: translateY(-5px);
|
||||||
|
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Formulários */
|
||||||
|
.form-grupo {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-grupo label {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--azul-escuro);
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-grupo input[type="text"],
|
||||||
|
.form-grupo input[type="email"],
|
||||||
|
.form-grupo textarea {
|
||||||
|
width: 100%;
|
||||||
|
padding: 12px;
|
||||||
|
border: 1px solid var(--cinza);
|
||||||
|
border-radius: var(--borda-radius);
|
||||||
|
transition: var(--transicao);
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-grupo input[type="text"]:focus,
|
||||||
|
.form-grupo input[type="email"]:focus,
|
||||||
|
.form-grupo textarea:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: var(--azul-principal);
|
||||||
|
box-shadow: 0 0 0 2px rgba(0, 100, 148, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Status do Ticket */
|
||||||
|
.status-badge {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 6px 12px;
|
||||||
|
border-radius: 20px;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-aberto {
|
||||||
|
background-color: var(--azul-claro);
|
||||||
|
color: var(--branco);
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-fechado {
|
||||||
|
background-color: var(--cinza);
|
||||||
|
color: var(--preto);
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-andamento {
|
||||||
|
background-color: var(--amarelo);
|
||||||
|
color: var(--preto);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Animações */
|
||||||
|
@keyframes fadeIn {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(20px);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.animate-fade-in {
|
||||||
|
animation: fadeIn 0.5s ease-out forwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Media Queries */
|
||||||
|
@media (max-width: 900px) {
|
||||||
|
.grid-layout {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ticket-card {
|
||||||
|
margin: 15px 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
.btn-primario {
|
||||||
|
display: block;
|
||||||
|
text-align: center;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
11
assets/js/frontend.js
Normal file
11
assets/js/frontend.js
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
const fileInput = document.getElementById('sts_ticket_attachment');
|
||||||
|
if (fileInput) {
|
||||||
|
fileInput.addEventListener('change', function() {
|
||||||
|
const fileNameSpan = document.getElementById('sts-file-name');
|
||||||
|
if (this.files.length > 0) {
|
||||||
|
fileNameSpan.textContent = this.files[0].name;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
185
includes/admin-ui.php
Normal file
185
includes/admin-ui.php
Normal file
@@ -0,0 +1,185 @@
|
|||||||
|
<?php
|
||||||
|
// Adicionar meta boxes para tickets
|
||||||
|
function sts_add_ticket_meta_boxes() {
|
||||||
|
add_meta_box(
|
||||||
|
'sts_ticket_details',
|
||||||
|
__('Detalhes do Ticket', 'simple-ticket-system'),
|
||||||
|
'sts_ticket_details_callback',
|
||||||
|
'ticket',
|
||||||
|
'side',
|
||||||
|
'high'
|
||||||
|
);
|
||||||
|
|
||||||
|
add_meta_box(
|
||||||
|
'sts_ticket_responses',
|
||||||
|
__('Respostas', 'simple-ticket-system'),
|
||||||
|
'sts_ticket_responses_callback',
|
||||||
|
'ticket',
|
||||||
|
'normal',
|
||||||
|
'high'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
add_action('add_meta_boxes', 'sts_add_ticket_meta_boxes');
|
||||||
|
|
||||||
|
// Callback para detalhes do ticket
|
||||||
|
function sts_ticket_details_callback($post) {
|
||||||
|
wp_nonce_field('sts_save_ticket_data', 'sts_ticket_nonce');
|
||||||
|
|
||||||
|
$status = wp_get_object_terms($post->ID, 'ticket_status', array('fields' => 'names'));
|
||||||
|
$current_status = !empty($status) ? $status[0] : 'Aberto';
|
||||||
|
|
||||||
|
$type = wp_get_object_terms($post->ID, 'ticket_type', array('fields' => 'names'));
|
||||||
|
$current_type = !empty($type) ? $type[0] : __('Nenhum', 'simple-ticket-system');
|
||||||
|
|
||||||
|
$author_id = $post->post_author;
|
||||||
|
$author = get_userdata($author_id);
|
||||||
|
$attachments = get_attached_media('', $post->ID);
|
||||||
|
$ticket_id = get_post_meta($post->ID, '_sts_ticket_id', true);
|
||||||
|
|
||||||
|
echo '<p><strong>' . __('ID do Ticket:', 'simple-ticket-system') . '</strong> ' . esc_html($ticket_id) . '</p>';
|
||||||
|
|
||||||
|
echo '<p><strong>' . __('Criado por:', 'simple-ticket-system') . '</strong> ' . $author->display_name . '</p>';
|
||||||
|
echo '<p><strong>' . __('Email:', 'simple-ticket-system') . '</strong> ' . $author->user_email . '</p>';
|
||||||
|
echo '<p><strong>' . __('Data:', 'simple-ticket-system') . '</strong> ' . get_the_date('d/m/Y H:i', $post->ID) . '</p>';
|
||||||
|
|
||||||
|
echo '<label for="ticket_status"><strong>' . __('Status:', 'simple-ticket-system') . '</strong></label>';
|
||||||
|
echo '<select name="ticket_status" id="ticket_status" style="width:100%">';
|
||||||
|
|
||||||
|
$statuses = get_terms(array(
|
||||||
|
'taxonomy' => 'ticket_status',
|
||||||
|
'hide_empty' => false
|
||||||
|
));
|
||||||
|
|
||||||
|
foreach ($statuses as $status) {
|
||||||
|
echo '<option value="' . $status->name . '" ' . selected($current_status, $status->name, false) . '>' . $status->name . '</option>';
|
||||||
|
}
|
||||||
|
|
||||||
|
echo '</select>';
|
||||||
|
|
||||||
|
echo '<p style="margin-top:10px;"><strong>' . __('Tipo de Solicitação:', 'simple-ticket-system') . '</strong> ' . esc_html($current_type) . '</p>';
|
||||||
|
|
||||||
|
if (!empty($attachments)) {
|
||||||
|
echo '<p style="margin-top:10px;"><strong>' . __('Anexos:', 'simple-ticket-system') . '</strong></p>';
|
||||||
|
echo '<ul>';
|
||||||
|
foreach ($attachments as $attachment) {
|
||||||
|
echo '<li><a href="' . wp_get_attachment_url($attachment->ID) . '" target="_blank">' . esc_html($attachment->post_title) . '</a></li>';
|
||||||
|
}
|
||||||
|
echo '</ul>';
|
||||||
|
} else {
|
||||||
|
echo '<p style="margin-top:10px;"><strong>' . __('Anexos:', 'simple-ticket-system') . '</strong> ' . __('Nenhum anexo.', 'simple-ticket-system') . '</p>';
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Callback para respostas do ticket
|
||||||
|
function sts_ticket_responses_callback($post) {
|
||||||
|
$responses = get_comments(array(
|
||||||
|
'post_id' => $post->ID,
|
||||||
|
'order' => 'ASC'
|
||||||
|
));
|
||||||
|
|
||||||
|
echo '<div class="sts-responses">';
|
||||||
|
|
||||||
|
if ($responses) {
|
||||||
|
foreach ($responses as $response) {
|
||||||
|
echo '<div class="sts-response">';
|
||||||
|
echo '<p><strong>' . $response->comment_author . '</strong> <em>' . date('d/m/Y H:i', strtotime($response->comment_date)) . '</em></p>';
|
||||||
|
echo '<div>' . wpautop($response->comment_content) . '</div>';
|
||||||
|
echo '</div><hr>';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
echo '<p>' . __('Nenhuma resposta ainda.', 'simple-ticket-system') . '</p>';
|
||||||
|
}
|
||||||
|
|
||||||
|
echo '</div>';
|
||||||
|
|
||||||
|
// Formulário para nova resposta
|
||||||
|
echo '<h3>' . __('Adicionar Resposta', 'simple-ticket-system') . '</h3>';
|
||||||
|
echo '<textarea name="sts_response_content" style="width:100%; height:100px;"></textarea>';
|
||||||
|
echo '<p class="description">' . __('Digite sua resposta acima e atualize o ticket.', 'simple-ticket-system') . '</p>';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Salvar dados do ticket
|
||||||
|
function sts_save_ticket_data($post_id) {
|
||||||
|
if (!isset($_POST['sts_ticket_nonce']) || !wp_verify_nonce($_POST['sts_ticket_nonce'], 'sts_save_ticket_data')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!current_user_can('edit_post', $post_id)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Salvar status
|
||||||
|
if (isset($_POST['ticket_status'])) {
|
||||||
|
$status_name = sanitize_text_field($_POST['ticket_status']);
|
||||||
|
$term = get_term_by('name', $status_name, 'ticket_status');
|
||||||
|
if ($term && !is_wp_error($term)) {
|
||||||
|
wp_set_object_terms($post_id, $term->term_id, 'ticket_status');
|
||||||
|
} else {
|
||||||
|
wp_set_object_terms($post_id, $status_name, 'ticket_status');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Salvar resposta se houver
|
||||||
|
if (!empty($_POST['sts_response_content'])) {
|
||||||
|
$user = wp_get_current_user();
|
||||||
|
|
||||||
|
$comment_data = array(
|
||||||
|
'comment_post_ID' => $post_id,
|
||||||
|
'comment_author' => $user->display_name,
|
||||||
|
'comment_author_email' => $user->user_email,
|
||||||
|
'comment_content' => wp_kses_post($_POST['sts_response_content']),
|
||||||
|
'comment_type' => '',
|
||||||
|
'comment_parent' => 0,
|
||||||
|
'user_id' => $user->ID,
|
||||||
|
'comment_approved' => 1,
|
||||||
|
);
|
||||||
|
|
||||||
|
$comment_id = wp_insert_comment($comment_data);
|
||||||
|
|
||||||
|
if ($comment_id) {
|
||||||
|
// Enviar notificação por email
|
||||||
|
sts_notify_ticket_response($post_id, $comment_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
add_action('save_post_ticket', 'sts_save_ticket_data', 10, 1);
|
||||||
|
|
||||||
|
// Personalizar colunas na lista de tickets
|
||||||
|
function sts_custom_ticket_columns($columns) {
|
||||||
|
$new_columns = array(
|
||||||
|
'cb' => $columns['cb'],
|
||||||
|
'ticket_id' => __('ID', 'simple-ticket-system'),
|
||||||
|
'title' => $columns['title'],
|
||||||
|
'author' => __('Autor', 'simple-ticket-system'),
|
||||||
|
'ticket_type' => __('Tipo', 'simple-ticket-system'),
|
||||||
|
'status' => __('Status', 'simple-ticket-system'),
|
||||||
|
'date' => $columns['date']
|
||||||
|
);
|
||||||
|
|
||||||
|
return $new_columns;
|
||||||
|
}
|
||||||
|
add_filter('manage_ticket_posts_columns', 'sts_custom_ticket_columns');
|
||||||
|
|
||||||
|
function sts_custom_ticket_column_data($column, $post_id) {
|
||||||
|
switch ($column) {
|
||||||
|
case 'status':
|
||||||
|
$status = wp_get_object_terms($post_id, 'ticket_status', array('fields' => 'names'));
|
||||||
|
if (!empty($status)) {
|
||||||
|
$status_class = sanitize_title($status[0]);
|
||||||
|
echo '<span class="sts-admin-status status-' . esc_attr($status_class) . '">' . esc_html($status[0]) . '</span>';
|
||||||
|
} else {
|
||||||
|
echo __('Nenhum status definido', 'simple-ticket-system');
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'ticket_id':
|
||||||
|
$ticket_id = get_post_meta($post_id, '_sts_ticket_id', true);
|
||||||
|
echo $ticket_id ? '<strong>' . esc_html($ticket_id) . '</strong>' : '';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
add_action('manage_ticket_posts_custom_column', 'sts_custom_ticket_column_data', 10, 2);
|
25
includes/assets.php
Normal file
25
includes/assets.php
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// Evitar acesso direto
|
||||||
|
if (!defined('ABSPATH')) {
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
function sts_enqueue_frontend_assets() {
|
||||||
|
// Enfileira o CSS do frontend
|
||||||
|
wp_enqueue_style('sts-frontend-style', STS_URL . 'assets/css/frontend.css', array(), STS_VERSION);
|
||||||
|
|
||||||
|
// Enfileira o JS do frontend
|
||||||
|
wp_enqueue_script('sts-frontend-script', STS_URL . 'assets/js/frontend.js', array(), STS_VERSION, true);
|
||||||
|
}
|
||||||
|
add_action('wp_enqueue_scripts', 'sts_enqueue_frontend_assets');
|
||||||
|
|
||||||
|
function sts_enqueue_admin_assets($hook) {
|
||||||
|
$screen = get_current_screen();
|
||||||
|
|
||||||
|
// Carrega o CSS apenas nas páginas de listagem e edição do post type 'ticket'
|
||||||
|
if (isset($screen->post_type) && $screen->post_type == 'ticket') {
|
||||||
|
wp_enqueue_style('sts-admin-style', STS_URL . 'assets/css/admin.css', array(), STS_VERSION);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
add_action('admin_enqueue_scripts', 'sts_enqueue_admin_assets');
|
99
includes/frontend.php
Normal file
99
includes/frontend.php
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
<?php
|
||||||
|
// Shortcode para formulário de abertura de ticket
|
||||||
|
function sts_ticket_form_shortcode() {
|
||||||
|
ob_start();
|
||||||
|
|
||||||
|
if (is_user_logged_in()) {
|
||||||
|
include STS_PATH . 'templates/form-ticket.php';
|
||||||
|
} else {
|
||||||
|
echo '<p>' . __('Você precisa estar logado para abrir um ticket.', 'simple-ticket-system') . '</p>';
|
||||||
|
echo wp_login_form(array('echo' => false));
|
||||||
|
}
|
||||||
|
|
||||||
|
return ob_get_clean();
|
||||||
|
}
|
||||||
|
add_shortcode('ticket_form', 'sts_ticket_form_shortcode');
|
||||||
|
|
||||||
|
// Shortcode para visualização de tickets do usuário
|
||||||
|
function sts_my_tickets_shortcode() {
|
||||||
|
ob_start();
|
||||||
|
|
||||||
|
if (is_user_logged_in()) {
|
||||||
|
include STS_PATH . 'templates/view-ticket.php';
|
||||||
|
} else {
|
||||||
|
echo '<p>' . __('Você precisa estar logado para visualizar seus tickets.', 'simple-ticket-system') . '</p>';
|
||||||
|
echo wp_login_form(array('echo' => false));
|
||||||
|
}
|
||||||
|
|
||||||
|
return ob_get_clean();
|
||||||
|
}
|
||||||
|
add_shortcode('my_tickets', 'sts_my_tickets_shortcode');
|
||||||
|
|
||||||
|
// Processar envio do formulário de ticket
|
||||||
|
function sts_process_ticket_form() {
|
||||||
|
if (isset($_POST['sts_submit_ticket']) && wp_verify_nonce($_POST['sts_ticket_form_nonce'], 'sts_ticket_form')) {
|
||||||
|
$current_user = wp_get_current_user();
|
||||||
|
$title = sanitize_text_field($_POST['sts_ticket_title']);
|
||||||
|
$content = wp_kses_post($_POST['sts_ticket_content']);
|
||||||
|
$type_id = isset($_POST['sts_ticket_type']) ? (int) $_POST['sts_ticket_type'] : 0;
|
||||||
|
|
||||||
|
$post_data = array(
|
||||||
|
'post_title' => $title,
|
||||||
|
'post_content' => $content,
|
||||||
|
'post_status' => 'publish',
|
||||||
|
'post_type' => 'ticket',
|
||||||
|
'post_author' => $current_user->ID
|
||||||
|
);
|
||||||
|
|
||||||
|
$post_id = wp_insert_post($post_data);
|
||||||
|
|
||||||
|
if (!is_wp_error($post_id)) {
|
||||||
|
// Gerar e salvar o ID personalizado
|
||||||
|
$last_id = get_option('sts_last_ticket_id', 0);
|
||||||
|
$new_id = $last_id + 1;
|
||||||
|
$ticket_id_formatted = 'DTI-' . sprintf('%03d', $new_id);
|
||||||
|
|
||||||
|
update_post_meta($post_id, '_sts_ticket_id', $ticket_id_formatted);
|
||||||
|
update_option('sts_last_ticket_id', $new_id);
|
||||||
|
|
||||||
|
// Definir status padrão
|
||||||
|
wp_set_object_terms($post_id, 'Aberto', 'ticket_status');
|
||||||
|
|
||||||
|
// Definir tipo de solicitação
|
||||||
|
if ($type_id > 0) {
|
||||||
|
wp_set_object_terms($post_id, $type_id, 'ticket_type');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lidar com o anexo
|
||||||
|
if (!empty($_FILES['sts_ticket_attachment']['name'])) {
|
||||||
|
require_once(ABSPATH . 'wp-admin/includes/file.php');
|
||||||
|
require_once(ABSPATH . 'wp-admin/includes/media.php');
|
||||||
|
require_once(ABSPATH . 'wp-admin/includes/image.php');
|
||||||
|
|
||||||
|
$attachment_id = media_handle_upload('sts_ticket_attachment', $post_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enviar notificação
|
||||||
|
sts_notify_new_ticket($post_id);
|
||||||
|
|
||||||
|
// Redireciona para a página do shortcode, não para o permalink do novo post.
|
||||||
|
wp_redirect(add_query_arg('ticket_submitted', 'success', wp_get_referer()));
|
||||||
|
exit;
|
||||||
|
} else {
|
||||||
|
wp_redirect(add_query_arg('ticket_submitted', 'error', wp_get_referer()));
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
add_action('init', 'sts_process_ticket_form');
|
||||||
|
|
||||||
|
// Carregar o template para a página de um único ticket
|
||||||
|
function sts_single_ticket_template($single_template) {
|
||||||
|
global $post;
|
||||||
|
|
||||||
|
if ($post->post_type == 'ticket') {
|
||||||
|
$single_template = STS_PATH . 'templates/single-ticket.php';
|
||||||
|
}
|
||||||
|
return $single_template;
|
||||||
|
}
|
||||||
|
add_filter('single_template', 'sts_single_ticket_template');
|
51
includes/notifications.php
Normal file
51
includes/notifications.php
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
<?php
|
||||||
|
// Notificar sobre novo ticket
|
||||||
|
function sts_notify_new_ticket($post_id) {
|
||||||
|
$post = get_post($post_id);
|
||||||
|
$author = get_userdata($post->post_author);
|
||||||
|
$admin_email = get_option('admin_email');
|
||||||
|
|
||||||
|
$subject = __('Novo Ticket Criado', 'simple-ticket-system') . ': ' . $post->post_title;
|
||||||
|
|
||||||
|
$message = __("Um novo ticket foi criado no sistema.\n\n", 'simple-ticket-system');
|
||||||
|
$message .= __('Título: ', 'simple-ticket-system') . $post->post_title . "\n";
|
||||||
|
$message .= __('Autor: ', 'simple-ticket-system') . $author->display_name . "\n";
|
||||||
|
$message .= __('Email: ', 'simple-ticket-system') . $author->user_email . "\n";
|
||||||
|
$message .= __('Conteúdo: ', 'simple-ticket-system') . "\n" . $post->post_content . "\n\n";
|
||||||
|
$message .= __('Para visualizar e responder ao ticket, acesse: ', 'simple-ticket-system') . admin_url('post.php?post=' . $post_id . '&action=edit') . "\n";
|
||||||
|
|
||||||
|
wp_mail($admin_email, $subject, $message);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Notificar sobre resposta ao ticket
|
||||||
|
function sts_notify_ticket_response($post_id, $comment_id) {
|
||||||
|
$post = get_post($post_id);
|
||||||
|
$comment = get_comment($comment_id);
|
||||||
|
$author = get_userdata($post->post_author);
|
||||||
|
|
||||||
|
// Notificar o autor do ticket
|
||||||
|
$subject = __('Nova Resposta no Seu Ticket', 'simple-ticket-system') . ': ' . $post->post_title;
|
||||||
|
|
||||||
|
$message = __("Seu ticket recebeu uma nova resposta.\n\n", 'simple-ticket-system');
|
||||||
|
$message .= __('Ticket: ', 'simple-ticket-system') . $post->post_title . "\n";
|
||||||
|
$message .= __('Resposta de: ', 'simple-ticket-system') . $comment->comment_author . "\n";
|
||||||
|
$message .= __('Resposta: ', 'simple-ticket-system') . "\n" . $comment->comment_content . "\n\n";
|
||||||
|
$message .= __('Para visualizar o ticket completo, acesse: ', 'simple-ticket-system') . get_permalink($post_id) . "\n";
|
||||||
|
|
||||||
|
wp_mail($author->user_email, $subject, $message);
|
||||||
|
|
||||||
|
// Se a resposta não foi do admin, notificar o admin
|
||||||
|
if (!user_can($comment->user_id, 'manage_options')) {
|
||||||
|
$admin_email = get_option('admin_email');
|
||||||
|
$subject = __('Nova Resposta no Ticket', 'simple-ticket-system') . ': ' . $post->post_title;
|
||||||
|
|
||||||
|
$message = __("Um ticket recebeu uma nova resposta.\n\n", 'simple-ticket-system');
|
||||||
|
$message .= __('Ticket: ', 'simple-ticket-system') . $post->post_title . "\n";
|
||||||
|
$message .= __('Autor do Ticket: ', 'simple-ticket-system') . $author->display_name . "\n";
|
||||||
|
$message .= __('Resposta de: ', 'simple-ticket-system') . $comment->comment_author . "\n";
|
||||||
|
$message .= __('Resposta: ', 'simple-ticket-system') . "\n" . $comment->comment_content . "\n\n";
|
||||||
|
$message .= __('Para visualizar e responder ao ticket, acesse: ', 'simple-ticket-system') . admin_url('post.php?post=' . $post_id . '&action=edit') . "\n";
|
||||||
|
|
||||||
|
wp_mail($admin_email, $subject, $message);
|
||||||
|
}
|
||||||
|
}
|
107
includes/post-types.php
Normal file
107
includes/post-types.php
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
<?php
|
||||||
|
// Registrar Custom Post Type para tickets
|
||||||
|
function sts_register_ticket_post_type() {
|
||||||
|
$labels = array(
|
||||||
|
'name' => __('Tickets', 'simple-ticket-system'),
|
||||||
|
'singular_name' => __('Ticket', 'simple-ticket-system'),
|
||||||
|
'menu_name' => __('Tickets', 'simple-ticket-system'),
|
||||||
|
'name_admin_bar' => __('Ticket', 'simple-ticket-system'),
|
||||||
|
'add_new' => __('Novo Ticket', 'simple-ticket-system'),
|
||||||
|
'add_new_item' => __('Adicionar Novo Ticket', 'simple-ticket-system'),
|
||||||
|
'new_item' => __('Novo Ticket', 'simple-ticket-system'),
|
||||||
|
'edit_item' => __('Editar Ticket', 'simple-ticket-system'),
|
||||||
|
'view_item' => __('Ver Ticket', 'simple-ticket-system'),
|
||||||
|
'all_items' => __('Todos os Tickets', 'simple-ticket-system'),
|
||||||
|
'search_items' => __('Procurar Tickets', 'simple-ticket-system'),
|
||||||
|
'not_found' => __('Nenhum ticket encontrado.', 'simple-ticket-system'),
|
||||||
|
'not_found_in_trash' => __('Nenhum ticket na lixeira.', 'simple-ticket-system')
|
||||||
|
);
|
||||||
|
|
||||||
|
$args = array(
|
||||||
|
'labels' => $labels,
|
||||||
|
'public' => true,
|
||||||
|
'has_archive' => false,
|
||||||
|
'publicly_queryable' => true,
|
||||||
|
'show_ui' => true,
|
||||||
|
'show_in_menu' => true,
|
||||||
|
'query_var' => true,
|
||||||
|
'rewrite' => array('slug' => 'ticket'),
|
||||||
|
'capability_type' => 'post',
|
||||||
|
'capabilities' => array(
|
||||||
|
'create_posts' => false, // Remover capacidade de criar diretamente
|
||||||
|
),
|
||||||
|
'map_meta_cap' => true,
|
||||||
|
'hierarchical' => false,
|
||||||
|
'menu_position' => null,
|
||||||
|
'supports' => array('title', 'editor', 'author'),
|
||||||
|
'menu_icon' => 'dashicons-tickets'
|
||||||
|
);
|
||||||
|
|
||||||
|
register_post_type('ticket', $args);
|
||||||
|
|
||||||
|
// Registrar taxonomia para status
|
||||||
|
$status_labels = array(
|
||||||
|
'name' => __('Status', 'simple-ticket-system'),
|
||||||
|
'singular_name' => __('Status', 'simple-ticket-system'),
|
||||||
|
'search_items' => __('Procurar Status', 'simple-ticket-system'),
|
||||||
|
'all_items' => __('Todos os Status', 'simple-ticket-system'),
|
||||||
|
'edit_item' => __('Editar Status', 'simple-ticket-system'),
|
||||||
|
'update_item' => __('Atualizar Status', 'simple-ticket-system'),
|
||||||
|
'add_new_item' => __('Adicionar Novo Status', 'simple-ticket-system'),
|
||||||
|
'new_item_name' => __('Novo Nome de Status', 'simple-ticket-system'),
|
||||||
|
'menu_name' => __('Status', 'simple-ticket-system'),
|
||||||
|
);
|
||||||
|
|
||||||
|
$status_args = array(
|
||||||
|
'hierarchical' => true,
|
||||||
|
'labels' => $status_labels,
|
||||||
|
'show_ui' => true,
|
||||||
|
'show_admin_column' => true,
|
||||||
|
'query_var' => true,
|
||||||
|
'rewrite' => array('slug' => 'ticket-status'),
|
||||||
|
);
|
||||||
|
|
||||||
|
register_taxonomy('ticket_status', 'ticket', $status_args);
|
||||||
|
|
||||||
|
// Registrar taxonomia para Tipo de Solicitação
|
||||||
|
$type_labels = array(
|
||||||
|
'name' => __('Tipos de Solicitação', 'simple-ticket-system'),
|
||||||
|
'singular_name' => __('Tipo de Solicitação', 'simple-ticket-system'),
|
||||||
|
'search_items' => __('Procurar Tipos', 'simple-ticket-system'),
|
||||||
|
'all_items' => __('Todos os Tipos', 'simple-ticket-system'),
|
||||||
|
'edit_item' => __('Editar Tipo', 'simple-ticket-system'),
|
||||||
|
'update_item' => __('Atualizar Tipo', 'simple-ticket-system'),
|
||||||
|
'add_new_item' => __('Adicionar Novo Tipo', 'simple-ticket-system'),
|
||||||
|
'new_item_name' => __('Novo Nome de Tipo', 'simple-ticket-system'),
|
||||||
|
'menu_name' => __('Tipos de Solicitação', 'simple-ticket-system'),
|
||||||
|
);
|
||||||
|
|
||||||
|
$type_args = array(
|
||||||
|
'hierarchical' => true,
|
||||||
|
'labels' => $type_labels,
|
||||||
|
'show_ui' => true,
|
||||||
|
'show_admin_column' => true,
|
||||||
|
'query_var' => true,
|
||||||
|
'rewrite' => array('slug' => 'ticket-type'),
|
||||||
|
);
|
||||||
|
|
||||||
|
register_taxonomy('ticket_type', 'ticket', $type_args);
|
||||||
|
|
||||||
|
// Adicionar status padrão
|
||||||
|
$default_statuses = array('Aberto', 'Em Andamento', 'Resolvido');
|
||||||
|
|
||||||
|
foreach ($default_statuses as $status) {
|
||||||
|
if (!term_exists($status, 'ticket_status')) {
|
||||||
|
wp_insert_term($status, 'ticket_status');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adicionar tipos padrão
|
||||||
|
$default_types = array('Dúvida', 'Problema Técnico', 'Sugestão');
|
||||||
|
foreach ($default_types as $type) {
|
||||||
|
if (!term_exists($type, 'ticket_type')) {
|
||||||
|
wp_insert_term($type, 'ticket_type');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
add_action('init', 'sts_register_ticket_post_type');
|
189
referencia.md
Normal file
189
referencia.md
Normal file
@@ -0,0 +1,189 @@
|
|||||||
|
### 1. **Variáveis CSS (Cores, Sombra, etc.)**
|
||||||
|
|
||||||
|
As variáveis são essenciais para a consistência do design e permitem que você altere rapidamente o estilo de toda a aplicação ao modificar o valor das variáveis. Algumas variáveis importantes que podem ser reutilizadas são:
|
||||||
|
|
||||||
|
```css
|
||||||
|
:root {
|
||||||
|
--azul-principal: #006494;
|
||||||
|
--azul-escuro: #003554;
|
||||||
|
--azul-claro: #0582ca;
|
||||||
|
--azul-brilhante: #00a6fb;
|
||||||
|
--amarelo: #ffb700;
|
||||||
|
--cinza-claro: #f8f9fa;
|
||||||
|
--cinza: #e9ecef;
|
||||||
|
--preto: #212529;
|
||||||
|
--branco: #ffffff;
|
||||||
|
--sombra: 0 4px 6px rgba(0, 0, 0, 0.1);
|
||||||
|
--borda-radius: 8px;
|
||||||
|
--transicao: all 0.3s ease;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Essas cores e efeitos podem ser reutilizados para manter o design consistente.
|
||||||
|
|
||||||
|
### 2. **Tipografia**
|
||||||
|
|
||||||
|
Fontes e tamanhos de texto podem ser extraídos e usados em outros projetos. Por exemplo:
|
||||||
|
|
||||||
|
```css
|
||||||
|
body {
|
||||||
|
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Você também tem diferentes tamanhos de fonte definidos nas regras de texto, como para o título de posts:
|
||||||
|
|
||||||
|
```css
|
||||||
|
.post-title {
|
||||||
|
font-size: 2.2rem;
|
||||||
|
color: var(--azul-escuro);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. **Layout Responsivo**
|
||||||
|
|
||||||
|
Esse código já tem um design responsivo com várias quebras de mídia. Você pode utilizar os padrões de grid e as regras de `@media` para garantir que seu projeto seja adaptável a diferentes tamanhos de tela. Um exemplo é o layout de grade no `.grid-layout`:
|
||||||
|
|
||||||
|
```css
|
||||||
|
.grid-layout {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 250px 1fr;
|
||||||
|
gap: 30px;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
E a adaptação responsiva para telas menores:
|
||||||
|
|
||||||
|
```css
|
||||||
|
@media (max-width: 900px) {
|
||||||
|
.grid-layout {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. **Estilos para Botões e Links**
|
||||||
|
|
||||||
|
Estilos para botões (`btn-primario`) e links (`.nav-principal a`, `.ver-tudo`) estão bem definidos e podem ser reutilizados:
|
||||||
|
|
||||||
|
```css
|
||||||
|
.btn-primario {
|
||||||
|
background: var(--branco);
|
||||||
|
color: var(--azul-principal);
|
||||||
|
padding: 12px 25px;
|
||||||
|
border-radius: var(--borda-radius);
|
||||||
|
font-weight: 600;
|
||||||
|
text-decoration: none;
|
||||||
|
transition: var(--transicao);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-principal a:hover:after {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Esses estilos garantem que o comportamento dos links e botões seja consistente, com efeitos de transição.
|
||||||
|
|
||||||
|
### 5. **Efeitos de Sombra e Transição**
|
||||||
|
|
||||||
|
As sombras e transições são definidos com variáveis e podem ser facilmente reutilizados:
|
||||||
|
|
||||||
|
```css
|
||||||
|
.sombra {
|
||||||
|
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
.sombra-hover {
|
||||||
|
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6. **Card Layouts (ex: Atalhos, Notícias)**
|
||||||
|
|
||||||
|
O estilo de cards, como no caso dos atalhos e das notícias, é bastante modular e pode ser reutilizado em diversos projetos. Por exemplo:
|
||||||
|
|
||||||
|
```css
|
||||||
|
.atalho-card {
|
||||||
|
background: var(--branco);
|
||||||
|
border-radius: var(--borda-radius);
|
||||||
|
padding: 25px 15px;
|
||||||
|
text-align: center;
|
||||||
|
box-shadow: var(--sombra);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.atalho-card:hover {
|
||||||
|
transform: translateY(-5px);
|
||||||
|
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 7. **Estilos de Ícones**
|
||||||
|
|
||||||
|
Estilos de ícones também são importantes, especialmente para se integrar com o Font Awesome:
|
||||||
|
|
||||||
|
```css
|
||||||
|
.atalho-card i {
|
||||||
|
font-size: 2rem;
|
||||||
|
color: var(--azul-principal);
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 8. **Componentes de Cabeçalho e Rodapé**
|
||||||
|
|
||||||
|
Esses componentes têm um estilo bem definido, com a possibilidade de facilmente aplicar em outros projetos:
|
||||||
|
|
||||||
|
```css
|
||||||
|
.header-moderno {
|
||||||
|
background: var(--branco);
|
||||||
|
box-shadow: var(--sombra);
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
z-index: 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer-moderno {
|
||||||
|
background: var(--azul-escuro);
|
||||||
|
color: var(--branco);
|
||||||
|
padding: 50px 0 20px;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 9. **Animações**
|
||||||
|
|
||||||
|
As animações, como o efeito `fadeIn`, são reutilizáveis e podem ser aplicadas a outros elementos:
|
||||||
|
|
||||||
|
```css
|
||||||
|
@keyframes fadeIn {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(20px);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.noticia-card, .stats-card, .atalho-card {
|
||||||
|
animation: fadeIn 0.5s ease-out forwards;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Resumo:
|
||||||
|
|
||||||
|
Você pode reutilizar e adaptar os seguintes elementos para outros projetos:
|
||||||
|
|
||||||
|
1. **Cores e Variáveis CSS** (cores principais, bordas, transições)
|
||||||
|
2. **Fontes e Tipografia** (fontes, tamanhos de texto)
|
||||||
|
3. **Layouts Responsivos** (uso de `@media` e grids)
|
||||||
|
4. **Botões e Links** (estilos interativos e transições)
|
||||||
|
5. **Sombreamento e Transições** (efeitos de sombra, hover)
|
||||||
|
6. **Card Layouts** (para atalhos, notícias, etc.)
|
||||||
|
7. **Ícones** (estilo e cores dos ícones)
|
||||||
|
8. **Componentes de Cabeçalho e Rodapé** (design de cabeçalhos fixos e rodapés)
|
||||||
|
9. **Animações** (efeitos de entrada, transições animadas)
|
||||||
|
|
||||||
|
|
49
simple-ticket-system.php
Normal file
49
simple-ticket-system.php
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Plugin Name: Simple Ticket System
|
||||||
|
* Plugin URI: https://exemplo.com
|
||||||
|
* Description: Um sistema simples de tickets para WordPress
|
||||||
|
* Version: 1.0.0
|
||||||
|
* Author: Seu Nome
|
||||||
|
* License: GPL v2 or later
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Evitar acesso direto
|
||||||
|
if (!defined('ABSPATH')) {
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Definir constantes
|
||||||
|
define('STS_PATH', plugin_dir_path(__FILE__));
|
||||||
|
define('STS_URL', plugin_dir_url(__FILE__));
|
||||||
|
define('STS_VERSION', '1.0.0');
|
||||||
|
|
||||||
|
// Incluir arquivos necessários
|
||||||
|
require_once STS_PATH . 'includes/post-types.php';
|
||||||
|
require_once STS_PATH . 'includes/admin-ui.php';
|
||||||
|
require_once STS_PATH . 'includes/frontend.php';
|
||||||
|
require_once STS_PATH . 'includes/notifications.php';
|
||||||
|
require_once STS_PATH . 'includes/assets.php';
|
||||||
|
|
||||||
|
// Ativar o plugin
|
||||||
|
register_activation_hook(__FILE__, 'sts_activate_plugin');
|
||||||
|
function sts_activate_plugin() {
|
||||||
|
// Criar tipos de post personalizados
|
||||||
|
sts_register_ticket_post_type();
|
||||||
|
|
||||||
|
// Recarregar regras de permalink
|
||||||
|
flush_rewrite_rules();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Desativar o plugin
|
||||||
|
register_deactivation_hook(__FILE__, 'sts_deactivate_plugin');
|
||||||
|
function sts_deactivate_plugin() {
|
||||||
|
flush_rewrite_rules();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inicializar o plugin
|
||||||
|
add_action('plugins_loaded', 'sts_init_plugin');
|
||||||
|
function sts_init_plugin() {
|
||||||
|
// Carregar traduções se necessário
|
||||||
|
load_plugin_textdomain('simple-ticket-system', false, dirname(plugin_basename(__FILE__)) . '/languages');
|
||||||
|
}
|
49
templates/form-ticket.php
Normal file
49
templates/form-ticket.php
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
<?php
|
||||||
|
if (isset($_GET['ticket_submitted'])) {
|
||||||
|
if ($_GET['ticket_submitted'] === 'success') {
|
||||||
|
echo '<div class="sts-alert sts-alert-success">' . __('Ticket enviado com sucesso!', 'simple-ticket-system') . '</div>';
|
||||||
|
} else {
|
||||||
|
echo '<div class="sts-alert sts-alert-error">' . __('Ocorreu um erro ao enviar o ticket.', 'simple-ticket-system') . '</div>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
|
||||||
|
<form action="" method="post" class="sts-ticket-form" enctype="multipart/form-data">
|
||||||
|
<?php wp_nonce_field('sts_ticket_form', 'sts_ticket_form_nonce'); ?>
|
||||||
|
|
||||||
|
<div class="form-grupo">
|
||||||
|
<label for="sts_ticket_title"><?php _e('Descreva resumidamente seu problema', 'simple-ticket-system'); ?></label>
|
||||||
|
<input type="text" id="sts_ticket_title" name="sts_ticket_title" required>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-grupo">
|
||||||
|
<label for="sts_ticket_content"><?php _e('Forneça detalhes', 'simple-ticket-system'); ?></label>
|
||||||
|
<textarea id="sts_ticket_content" name="sts_ticket_content" rows="8" required></textarea>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-grupo">
|
||||||
|
<label for="sts_ticket_type"><?php _e('Tipo de solicitação', 'simple-ticket-system'); ?></label>
|
||||||
|
<select id="sts_ticket_type" name="sts_ticket_type" required>
|
||||||
|
<?php
|
||||||
|
$types = get_terms(array('taxonomy' => 'ticket_type', 'hide_empty' => false));
|
||||||
|
foreach ($types as $type) {
|
||||||
|
echo '<option value="' . esc_attr($type->term_id) . '">' . esc_html($type->name) . '</option>';
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-grupo">
|
||||||
|
<label for="sts_ticket_attachment"><?php _e('Anexo', 'simple-ticket-system'); ?></label>
|
||||||
|
<div class="sts-file-input-wrapper">
|
||||||
|
<span class="sts-file-input-button"><?php _e('Escolher arquivo', 'simple-ticket-system'); ?></span>
|
||||||
|
<input type="file" id="sts_ticket_attachment" name="sts_ticket_attachment">
|
||||||
|
</div>
|
||||||
|
<span id="sts-file-name" class="sts-file-name"></span>
|
||||||
|
<small><?php _e('(captura de tela, documento ou qualquer outro arquivo)', 'simple-ticket-system'); ?></small>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-grupo">
|
||||||
|
<input type="submit" name="sts_submit_ticket" value="<?php _e('Enviar Ticket', 'simple-ticket-system'); ?>">
|
||||||
|
</div>
|
||||||
|
</form>
|
66
templates/single-ticket.php
Normal file
66
templates/single-ticket.php
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
<?php
|
||||||
|
get_header(); ?>
|
||||||
|
|
||||||
|
<div id="primary" class="sts-content-area">
|
||||||
|
<main id="main" class="sts-site-main">
|
||||||
|
|
||||||
|
<?php
|
||||||
|
while (have_posts()) : the_post();
|
||||||
|
$status = wp_get_object_terms(get_the_ID(), 'ticket_status', array('fields' => 'names'));
|
||||||
|
$types = wp_get_object_terms(get_the_ID(), 'ticket_type', array('fields' => 'names'));
|
||||||
|
$attachments = get_attached_media('', get_the_ID());
|
||||||
|
$ticket_id = get_post_meta(get_the_ID(), '_sts_ticket_id', true);
|
||||||
|
|
||||||
|
$current_status = !empty($status) ? esc_html($status[0]) : __('Sem status', 'simple-ticket-system');
|
||||||
|
$status_class = !empty($status) ? sanitize_title($status[0]) : 'no-status';
|
||||||
|
$current_type = !empty($types) ? esc_html($types[0]) : __('Não definido', 'simple-ticket-system');
|
||||||
|
?>
|
||||||
|
|
||||||
|
<article id="post-<?php the_ID(); ?>" <?php post_class('sts-single-ticket'); ?>>
|
||||||
|
<header class="sts-ticket-header">
|
||||||
|
<div class="sts-title-wrapper">
|
||||||
|
<h1 class="sts-ticket-title"><?php the_title(); ?></h1>
|
||||||
|
<span class="sts-single-ticket-id"><?php echo esc_html($ticket_id); ?></span>
|
||||||
|
</div>
|
||||||
|
<div class="sts-ticket-meta">
|
||||||
|
<span class="sts-ticket-status <?php echo $status_class; ?>"><?php echo $current_status; ?></span>
|
||||||
|
<span class="sts-ticket-info"><strong><?php _e('Tipo:', 'simple-ticket-system'); ?></strong> <?php echo $current_type; ?></span>
|
||||||
|
<span class="sts-ticket-info"><strong><?php _e('Criado em:', 'simple-ticket-system'); ?></strong> <?php echo get_the_date('d/m/Y H:i'); ?></span>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div class="sts-ticket-content">
|
||||||
|
<h3><?php _e('Descrição do Problema', 'simple-ticket-system'); ?></h3>
|
||||||
|
<?php the_content(); ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php if (!empty($attachments)) : ?>
|
||||||
|
<div class="sts-ticket-attachments">
|
||||||
|
<h3><?php _e('Anexos', 'simple-ticket-system'); ?></h3>
|
||||||
|
<ul>
|
||||||
|
<?php foreach ($attachments as $attachment) : ?>
|
||||||
|
<li><a href="<?php echo wp_get_attachment_url($attachment->ID); ?>" target="_blank"><?php echo esc_html($attachment->post_title); ?></a></li>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<div class="sts-ticket-responses">
|
||||||
|
<?php
|
||||||
|
// Se os comentários estiverem abertos ou se houver pelo menos um comentário, carregue o template de comentários.
|
||||||
|
if (comments_open() || get_comments_number()) :
|
||||||
|
comments_template();
|
||||||
|
endif;
|
||||||
|
?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<?php endwhile; ?>
|
||||||
|
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php
|
||||||
|
get_footer();
|
||||||
|
?>
|
47
templates/view-ticket.php
Normal file
47
templates/view-ticket.php
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
<?php
|
||||||
|
$current_user = wp_get_current_user();
|
||||||
|
$args = array(
|
||||||
|
'post_type' => 'ticket',
|
||||||
|
'author' => $current_user->ID,
|
||||||
|
'posts_per_page' => -1,
|
||||||
|
'orderby' => 'date',
|
||||||
|
'order' => 'DESC'
|
||||||
|
);
|
||||||
|
|
||||||
|
$tickets = get_posts($args);
|
||||||
|
|
||||||
|
if ($tickets) {
|
||||||
|
echo '<h3 class="sts-section-title">' . __('Meus Tickets', 'simple-ticket-system') . '</h3>';
|
||||||
|
echo '<div class="sts-ticket-grid">';
|
||||||
|
|
||||||
|
foreach ($tickets as $ticket) {
|
||||||
|
$status = wp_get_object_terms($ticket->ID, 'ticket_status', array('fields' => 'names'));
|
||||||
|
$types = wp_get_object_terms($ticket->ID, 'ticket_type', array('fields' => 'names'));
|
||||||
|
$ticket_id = get_post_meta($ticket->ID, '_sts_ticket_id', true);
|
||||||
|
|
||||||
|
$status_class = !empty($status) ? sanitize_title($status[0]) : 'no-status';
|
||||||
|
$current_status = !empty($status) ? esc_html($status[0]) : __('Sem status', 'simple-ticket-system');
|
||||||
|
$current_type = !empty($types) ? esc_html($types[0]) : __('Não definido', 'simple-ticket-system');
|
||||||
|
|
||||||
|
echo '<a href="' . get_permalink($ticket->ID) . '" class="sts-ticket-card ' . $status_class . '">';
|
||||||
|
echo '<span class="sts-card-ticket-id">' . esc_html($ticket_id) . '</span>';
|
||||||
|
echo '<div class="sts-card-header">';
|
||||||
|
echo '<h4 class="sts-card-title">' . esc_html($ticket->post_title) . '</h4>';
|
||||||
|
echo '<span class="sts-ticket-status ' . $status_class . '">' . $current_status . '</span>';
|
||||||
|
echo '</div>';
|
||||||
|
|
||||||
|
echo '<div class="sts-card-body">';
|
||||||
|
echo '<p><strong>' . __('Tipo:', 'simple-ticket-system') . '</strong> ' . $current_type . '</p>';
|
||||||
|
echo '</div>';
|
||||||
|
|
||||||
|
echo '<div class="sts-card-footer">';
|
||||||
|
echo '<span class="sts-ticket-date">' . sprintf(__('Aberto em: %s', 'simple-ticket-system'), date_i18n('d/m/Y', strtotime($ticket->post_date))) . '</span>';
|
||||||
|
echo '</div>';
|
||||||
|
|
||||||
|
echo '</a>';
|
||||||
|
}
|
||||||
|
|
||||||
|
echo '</div>';
|
||||||
|
} else {
|
||||||
|
echo '<p>' . __('Você não possui tickets.', 'simple-ticket-system') . '</p>';
|
||||||
|
}
|
Reference in New Issue
Block a user