Added a ticket queues object to organize tickets; added ability to change a ticket's queue
This commit is contained in:
parent
15ee9b78a3
commit
80a12a86ef
@ -1,12 +1,41 @@
|
||||
$(document).ready ->
|
||||
console.log('Hello, world!')
|
||||
|
||||
$('.ticket-severity').on('click', handleAttributeClick('severity'))
|
||||
$('.ticket-status').on('click', handleAttributeClick('status'))
|
||||
$('.ticket-queue').on('click', (e) ->
|
||||
handleQueueClick(e)
|
||||
)
|
||||
$('.ticket-severity').on('click', (e) ->
|
||||
handleAttributeClick(e, 'severity')
|
||||
)
|
||||
$('.ticket-status').on('click', (e) ->
|
||||
handleAttributeClick(e, 'status')
|
||||
)
|
||||
|
||||
validOptions =
|
||||
'severity' = ['low', 'medium', 'high'],
|
||||
'status' = ['open', 'closed', 'parked']
|
||||
'severity': ['low', 'medium', 'high'],
|
||||
'status': ['open', 'closed', 'parked']
|
||||
|
||||
handleQueueClick = (e, fail = false) ->
|
||||
newQueueId = prompt('Set queue ID:', $('.ticket-queue').data('id'))
|
||||
|
||||
if (newQueueId != null) and (newQueueId != '')
|
||||
if (true)
|
||||
console.log('Setting queue ID to ' + newQueueId)
|
||||
editLink = $('#ticketEditLink').attr('href') + '/queue_id'
|
||||
console.log('Sending data to ' + editLink)
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: editLink,
|
||||
data:
|
||||
'queue_id': newQueueId,
|
||||
dataType: 'json',
|
||||
success: (result) ->
|
||||
$('.ticket-queue').data('id', newQueueId)
|
||||
$('.ticket-queue > span').text(result.queue_name)
|
||||
updateTicketModified(result.updated_at)
|
||||
console.log('Ticket updated successfully.')
|
||||
})
|
||||
else
|
||||
console.log('Invalid queue ID entered')
|
||||
handleQueueClick(e, 'Invalid queue ID entered; you must enter a number.')
|
||||
|
||||
handleAttributeClick = (e, attr, fail = false) ->
|
||||
newValue = prompt('Set ticket ' + attr + ':', $('.ticket-' + attr + ' > span').text())
|
||||
@ -16,12 +45,13 @@ handleAttributeClick = (e, attr, fail = false) ->
|
||||
if (newValue in validOptions[attr])
|
||||
console.log('Setting ' + attr + ' to ' + newValue)
|
||||
editLink = $('#ticketEditLink').attr('href') + '/' + attr
|
||||
postData = {}
|
||||
postData[attr] = newValue
|
||||
console.log('Sending data to ' + editLink)
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: editLink,
|
||||
data:
|
||||
attr: newValue,
|
||||
data: postData,
|
||||
dataType: 'json',
|
||||
success: (result) ->
|
||||
newValue = newValue.charAt(0).toUpperCase() + newValue.slice(1)
|
||||
|
@ -81,12 +81,16 @@ input[type="submit"].button-primary
|
||||
height: 250px
|
||||
min-height: 100px
|
||||
|
||||
#queue-header,
|
||||
#ticket-header
|
||||
margin-bottom: 15px
|
||||
|
||||
.queue-title,
|
||||
.ticket-title
|
||||
margin-bottom: 5px
|
||||
|
||||
.queue-created,
|
||||
.queue-updated,
|
||||
.ticket-created,
|
||||
.ticket-updated
|
||||
margin-bottom: 3px
|
||||
@ -94,6 +98,7 @@ input[type="submit"].button-primary
|
||||
font-size: 1.5rem
|
||||
font-style: italic
|
||||
|
||||
#queue-description,
|
||||
#ticket-body
|
||||
p:last-child
|
||||
margin-bottom: 5px
|
||||
@ -141,6 +146,7 @@ input[type="submit"].button-primary
|
||||
margin-right: 5px
|
||||
font-size: 2rem
|
||||
|
||||
.ticket-queue,
|
||||
.ticket-severity,
|
||||
.ticket-status
|
||||
transition: all 230ms ease-in-out
|
||||
|
35
db/migrations/20221204223428_add_queue_table.php
Normal file
35
db/migrations/20221204223428_add_queue_table.php
Normal file
@ -0,0 +1,35 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
use Phinx\Migration\AbstractMigration;
|
||||
|
||||
final class AddQueueTable extends AbstractMigration
|
||||
{
|
||||
/**
|
||||
* Change Method.
|
||||
*
|
||||
* Write your reversible migrations using this method.
|
||||
*
|
||||
* More information on writing migrations is available here:
|
||||
* https://book.cakephp.org/phinx/0/en/migrations.html#the-change-method
|
||||
*
|
||||
* Remember to call "create()" or "update()" and NOT "save()" when working
|
||||
* with the Table class.
|
||||
*/
|
||||
public function change()
|
||||
{
|
||||
// Create table for Queue objects
|
||||
$table = $this->table('queues');
|
||||
$table->addColumn('title', 'string', ['null' => false])
|
||||
->addColumn('description', 'text', ['null' => false])
|
||||
->addTimestamps()
|
||||
->addIndex(['title'])
|
||||
->create();
|
||||
|
||||
// Update tickets table to have a Queue ID field
|
||||
$tickets = $this->table('tickets')
|
||||
->addColumn('queue_id', 'integer', ['null' => false])
|
||||
->addForeignKey('queue_id', 'queues', 'id', ['delete' => 'CASCADE', 'update' => 'CASCADE'])
|
||||
->update();
|
||||
}
|
||||
}
|
44
src/Controllers/QueueController.php
Normal file
44
src/Controllers/QueueController.php
Normal file
@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
namespace BitGoblin\Goliath\Controllers;
|
||||
|
||||
use Psr\Http\Message\ResponseInterface as Response;
|
||||
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||
use Slim\Routing\RouteContext;
|
||||
use Slim\Views\Twig;
|
||||
use BitGoblin\Goliath\Models\Queue;
|
||||
|
||||
class QueueController extends Controller {
|
||||
|
||||
public function getView(Request $request, Response $response, array $args): Response {
|
||||
$queue = Queue::where('id', $args['queue_id'])->first();
|
||||
|
||||
$view = Twig::fromRequest($request);
|
||||
return $view->render($response, 'queue/view.twig', [
|
||||
'queue' => $queue,
|
||||
]);
|
||||
}
|
||||
|
||||
public function getCreate(Request $request, Response $response): Response {
|
||||
$view = Twig::fromRequest($request);
|
||||
return $view->render($response, 'queue/create.twig');
|
||||
}
|
||||
|
||||
public function postCreate(Request $request, Response $response): Response {
|
||||
$params = (array)$request->getParsedBody();
|
||||
|
||||
$queue = new Queue;
|
||||
$queue->title = $params['queue_title'];
|
||||
$queue->description = $params['queue_description'];
|
||||
|
||||
$queue->save();
|
||||
|
||||
// redirect the user back to the home page
|
||||
$routeContext = RouteContext::fromRequest($request);
|
||||
$routeParser = $routeContext->getRouteParser();
|
||||
return $response
|
||||
->withHeader('Location', $routeParser->urlFor('queue.view', ['queue_id' => $queue->id]))
|
||||
->withStatus(302);
|
||||
}
|
||||
|
||||
}
|
@ -81,11 +81,17 @@ class TicketController extends Controller {
|
||||
// save updated ticket
|
||||
$ticket->save();
|
||||
|
||||
// return a response
|
||||
$response->getBody()->write(json_encode([
|
||||
// build JSON response
|
||||
$jsonResponse = [
|
||||
'result' => 'success',
|
||||
'updated_at' => $ticket->formatUpdatedAt(),
|
||||
]));
|
||||
];
|
||||
if ($attr == 'queue_id') {
|
||||
$jsonResponse['queue_name'] = $ticket->queue->title;
|
||||
}
|
||||
|
||||
// return a response
|
||||
$response->getBody()->write(json_encode($jsonResponse));
|
||||
return $response;
|
||||
}
|
||||
|
||||
|
36
src/Models/Queue.php
Normal file
36
src/Models/Queue.php
Normal file
@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace BitGoblin\Goliath\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use League\CommonMark\CommonMarkConverter;
|
||||
|
||||
class Queue extends Model {
|
||||
|
||||
protected $fillable = [
|
||||
'title',
|
||||
'description',
|
||||
];
|
||||
|
||||
public function tickets() {
|
||||
return $this->hasMany(Ticket::class);
|
||||
}
|
||||
|
||||
public function render(): string {
|
||||
$converter = new CommonMarkConverter([
|
||||
'html_input' => 'strip',
|
||||
'allow_unsafe_links' => false,
|
||||
]);
|
||||
|
||||
return $converter->convert($this->description);
|
||||
}
|
||||
|
||||
public function formatCreatedAt(): string {
|
||||
return date_format(date_create($this->created_at), "F jS\\, Y \\a\\t g:i:s a");
|
||||
}
|
||||
|
||||
public function formatUpdatedAt(): string {
|
||||
return date_format(date_create($this->updated_at), "F jS\\, Y \\a\\t g:i:s a");
|
||||
}
|
||||
|
||||
}
|
@ -14,6 +14,14 @@ class Ticket extends Model {
|
||||
'due_at',
|
||||
];
|
||||
|
||||
public function queue() {
|
||||
return $this->belongsTo(Queue::class);
|
||||
}
|
||||
|
||||
public function comments() {
|
||||
return $this->hasMany(Comment::class);
|
||||
}
|
||||
|
||||
public function render(): string {
|
||||
$converter = new CommonMarkConverter([
|
||||
'html_input' => 'strip',
|
||||
@ -23,10 +31,6 @@ class Ticket extends Model {
|
||||
return $converter->convert($this->body);
|
||||
}
|
||||
|
||||
public function comments() {
|
||||
return $this->hasMany(Comment::class);
|
||||
}
|
||||
|
||||
public function formatCreatedAt(): string {
|
||||
return date_format(date_create($this->created_at), "F jS\\, Y \\a\\t g:i:s a");
|
||||
}
|
||||
|
@ -7,6 +7,13 @@ use Slim\Views\Twig;
|
||||
// index GET route - this page should welcome the user and direct them to the available actions
|
||||
$app->get('/', '\\BitGoblin\\Goliath\\Controllers\\HomeController:getIndex')->setName('index');
|
||||
|
||||
// /queue/create routes - allows a user to fill out a form to create a new queue
|
||||
$app->get('/queue/create', '\\BitGoblin\\Goliath\\Controllers\\QueueController:getCreate')->setName('queue.create');
|
||||
$app->post('/queue/create', '\\BitGoblin\\Goliath\\Controllers\\QueueController:postCreate');
|
||||
|
||||
// /queue/id route - displays queue info to user
|
||||
$app->get('/queue/{queue_id}', '\\BitGoblin\\Goliath\\Controllers\QueueController:getView')->setName('queue.view');
|
||||
|
||||
// /ticket/create routes - allows a user to fill out a form to create a ticket
|
||||
$app->get('/ticket/create', '\\BitGoblin\\Goliath\\Controllers\\TicketController:getCreate')->setName('ticket.create');
|
||||
$app->post('/ticket/create', '\\BitGoblin\\Goliath\\Controllers\\TicketController:postCreate');
|
||||
|
@ -16,7 +16,8 @@
|
||||
<ul class="nav-menu">
|
||||
<li>Goliath</li>
|
||||
<li class="nav-link"><a href="{{ url_for('index') }}">Home</a></li>
|
||||
<li class="nav-link"><a href="{{ url_for('ticket.create') }}">Create</a></li>
|
||||
<li class="nav-link"><a href="{{ url_for('queue.create') }}">New Queue</a></li>
|
||||
<li class="nav-link"><a href="{{ url_for('ticket.create') }}">New Ticket</a></li>
|
||||
<li class="nav-link"><a href="/search">Search</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
29
views/queue/create.twig
Normal file
29
views/queue/create.twig
Normal file
@ -0,0 +1,29 @@
|
||||
{% extends 'layout.twig' %}
|
||||
|
||||
{% block title %}Create New Queue{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="columns twelve">
|
||||
<h1>Create new queue</h1>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="columns twelve">
|
||||
<form id="queue-form" action="/queue/create" method="POST" class="u-full-width">
|
||||
<div class="row">
|
||||
<div class="twelve columns">
|
||||
<label for="queue_title">Title</label>
|
||||
<input id="queue_title" class="u-full-width" type="text" placeholder="My new queue" name="queue_title">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<label for="queue_description">Description</label>
|
||||
<textarea id="queue_description" class="u-full-width" placeholder="Explain what this queue is about..." name="queue_description"></textarea>
|
||||
|
||||
<input class="button-primary u-full-width" type="submit" value="Submit">
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
44
views/queue/view.twig
Normal file
44
views/queue/view.twig
Normal file
@ -0,0 +1,44 @@
|
||||
{% extends 'layout.twig' %}
|
||||
|
||||
{% block title %}Ticket Queue: {{ queue.title }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<!-- queue content -->
|
||||
<div class="row">
|
||||
<div class="twelve columns">
|
||||
<div id="queue-header" class="row">
|
||||
<div class="columns twelve">
|
||||
<h1 class="queue-title">{{ queue.title }}</h1>
|
||||
<h4 class="queue-created">Created at: <span>{{ queue.formatCreatedAt() }}</span></h4>
|
||||
<h4 class="queue-updated">Last updated at: <span>{{ queue.formatUpdatedAt() }}</span></h4>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="queue-description" class="row">
|
||||
<div class="columns twelve">
|
||||
{{ queue.render() | raw }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<!-- queue tickets -->
|
||||
<div class="row">
|
||||
<div class="twelve columns">
|
||||
<ul id="queue-list" class="row">
|
||||
<h3>Tickets in this queue:</h3>
|
||||
{% if queue.tickets | length > 0 %}
|
||||
{% for ticket in queue.tickets %}
|
||||
<li>
|
||||
<a href="{{ url_for('ticket.view', { ticket_id: ticket.id }) }}">{{ ticket.title }}</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<p>There are no tickets in this queue.</p>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
@ -29,6 +29,9 @@
|
||||
<li><a id="ticketEditLink" href="{{ url_for('ticket.edit', {ticket_id: ticket.id}) }}"><i class="fa-solid fa-pen-to-square"></i>Edit</a></li><li><a href="{{ url_for('ticket.delete', {ticket_id: ticket.id}) }}"><i class="fa-solid fa-trash"></i>Delete</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="ticket-queue" data-id="{{ ticket.queue.id }}">
|
||||
Queue: <span>{{ ticket.queue.title }}</span>
|
||||
</li>
|
||||
<li class="ticket-severity">
|
||||
Severity: <span>{{ ticket.severity | capitalize }}</span>
|
||||
</li>
|
||||
|
Loading…
Reference in New Issue
Block a user