ticket history and tags
This commit is contained in:
parent
916657e09c
commit
0bc558d4d7
@ -36,7 +36,20 @@ class TicketController extends BaseController implements CRUD {
|
|||||||
$ticket_id = $f3->get('PARAMS.id');
|
$ticket_id = $f3->get('PARAMS.id');
|
||||||
$ticket_mapper = new Ticket($this->getDB());
|
$ticket_mapper = new Ticket($this->getDB());
|
||||||
$ticket = $ticket_mapper->findById($ticket_id);
|
$ticket = $ticket_mapper->findById($ticket_id);
|
||||||
|
|
||||||
|
if(!$ticket){
|
||||||
|
$this->f3->set('SESSION.error', 'Ticket not found');
|
||||||
|
$this->f3->reroute('/tickets');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
$assigned_user = $ticket->getAssignedUser();
|
$assigned_user = $ticket->getAssignedUser();
|
||||||
|
$ticket_history = $ticket->getHistory();
|
||||||
|
|
||||||
|
$map_statuses = array_column((new TicketStatus($this->getDB()))->findAll(), 'name', 'id');
|
||||||
|
$map_priorities = array_column((new TicketStatus($this->getDB()))->findAll(), 'name', 'id');
|
||||||
|
$map_users = array_column($this->getDB()->exec('SELECT id, display_name FROM users'), 'display_name', 'id');
|
||||||
|
|
||||||
|
|
||||||
// render
|
// render
|
||||||
$this->renderView('views/ticket/view.html', [
|
$this->renderView('views/ticket/view.html', [
|
||||||
@ -46,7 +59,13 @@ class TicketController extends BaseController implements CRUD {
|
|||||||
'comments' => $ticket->comments(),
|
'comments' => $ticket->comments(),
|
||||||
'parent_tickets' => $ticket->getParentTickets(),
|
'parent_tickets' => $ticket->getParentTickets(),
|
||||||
'child_tickets' => $ticket->getChildTickets(),
|
'child_tickets' => $ticket->getChildTickets(),
|
||||||
'ticket_meta' => $ticket->getMetaAssoc()
|
'ticket_meta' => $ticket->getMetaAssoc(),
|
||||||
|
'ticket_history' => $ticket_history,
|
||||||
|
'map' => [
|
||||||
|
'statuses' => $map_statuses,
|
||||||
|
'priorities' => $map_priorities,
|
||||||
|
'users' => $map_users
|
||||||
|
]
|
||||||
]);
|
]);
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -58,6 +77,9 @@ class TicketController extends BaseController implements CRUD {
|
|||||||
$priorities = (new TicketPriority($db))->findAll();
|
$priorities = (new TicketPriority($db))->findAll();
|
||||||
$statuses = (new TicketStatus($db))->findAll();
|
$statuses = (new TicketStatus($db))->findAll();
|
||||||
|
|
||||||
|
$all_tags_model = new \Tag($this->getDB());
|
||||||
|
$all_tags = $all_tags_model->find([], ['order' => 'name ASC']); // get all tags
|
||||||
|
|
||||||
// TODO: this needs moving into a model?
|
// TODO: this needs moving into a model?
|
||||||
$users = $this->getDB()->exec('SELECT id, username, display_name FROM users ORDER BY display_name ASC');
|
$users = $this->getDB()->exec('SELECT id, username, display_name FROM users ORDER BY display_name ASC');
|
||||||
$users = array_merge([['id'=>'-1', 'display_name'=>'--']], $users);
|
$users = array_merge([['id'=>'-1', 'display_name'=>'--']], $users);
|
||||||
@ -66,7 +88,8 @@ class TicketController extends BaseController implements CRUD {
|
|||||||
$this->renderView('views/ticket/create.html',[
|
$this->renderView('views/ticket/create.html',[
|
||||||
'priorities' => $priorities,
|
'priorities' => $priorities,
|
||||||
'statuses' => $statuses,
|
'statuses' => $statuses,
|
||||||
'users' => $users
|
'users' => $users,
|
||||||
|
'all_tags' => $all_tags
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -83,18 +106,29 @@ class TicketController extends BaseController implements CRUD {
|
|||||||
'description' => $this->f3->get('POST.description'),
|
'description' => $this->f3->get('POST.description'),
|
||||||
'priority_id' => $this->f3->get('POST.priority_id'),
|
'priority_id' => $this->f3->get('POST.priority_id'),
|
||||||
'status_id' => $this->f3->get('POST.status_id'),
|
'status_id' => $this->f3->get('POST.status_id'),
|
||||||
'created_by' => $this->f3->get('SESSION.user.id')
|
'created_by' => $this->f3->get('SESSION.user.id'),
|
||||||
|
'assigned_to' => $this->f3->get('POST.assigned_to') == '-1' ? null : $this->f3->get('POST.assigned_to')
|
||||||
];
|
];
|
||||||
|
|
||||||
$ticket_mapper = new Ticket($this->getDB());
|
$ticket_mapper = new Ticket($this->getDB());
|
||||||
$new_ticket_id = $ticket_mapper->createTicket($data);
|
$new_ticket_id = $ticket_mapper->createTicket($data);
|
||||||
|
|
||||||
// custom field
|
// custom field
|
||||||
$meta_keys = $this->f3->get('POST.meta_key');
|
// $meta_keys = $this->f3->get('POST.meta_key');
|
||||||
$meta_values = $this->f3->get('POST.meta_value');
|
// $meta_values = $this->f3->get('POST.meta_value');
|
||||||
$meta_assoc = $ticket_mapper->assocMetaFromKeyValue($meta_keys, $meta_values);
|
// $meta_assoc = $ticket_mapper->assocMetaFromKeyValue($meta_keys, $meta_values);
|
||||||
$ticket_mapper->setCustomFields($meta_assoc);
|
// $ticket_mapper->setCustomFields($meta_assoc);
|
||||||
|
|
||||||
|
$new_ticket = $ticket_mapper->findById($new_ticket_id);
|
||||||
|
if($new_ticket){
|
||||||
|
// TAG handling for create
|
||||||
|
$posted_tags = $this->f3->get('POST.tags');
|
||||||
|
if(!empty($posted_tags) && is_array($posted_tags)){
|
||||||
|
$new_ticket->setTags($posted_tags);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->f3->set('SESSION.message', 'Ticket #' . $new_ticket_id . ' created successfully.');
|
||||||
$this->f3->reroute('/ticket/' . $new_ticket_id);
|
$this->f3->reroute('/ticket/' . $new_ticket_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -120,16 +154,29 @@ class TicketController extends BaseController implements CRUD {
|
|||||||
// dropdowns
|
// dropdowns
|
||||||
$priorities = (new TicketPriority($this->getDB()))->findAll();
|
$priorities = (new TicketPriority($this->getDB()))->findAll();
|
||||||
$statuses = (new TicketStatus($this->getDB()))->findAll();
|
$statuses = (new TicketStatus($this->getDB()))->findAll();
|
||||||
|
$all_tags_model = new \Tag($this->getDB());
|
||||||
|
$all_tags = $all_tags_model->find([], ['order' => 'name ASC']);
|
||||||
|
|
||||||
// TODO: this needs moving into a model?
|
// TODO: this needs moving into a model?
|
||||||
$users = $this->getDB()->exec('SELECT id, username, display_name FROM users ORDER BY display_name ASC');
|
$users = $this->getDB()->exec('SELECT id, username, display_name FROM users ORDER BY display_name ASC');
|
||||||
$users = array_merge([['id'=>'-1', 'display_name'=>'--']], $users);
|
$users = array_merge([['id'=>'-1', 'display_name'=>'--']], $users);
|
||||||
|
|
||||||
|
// paradox - empty($ticket->tags) was returning true, when there were items present
|
||||||
|
$current_ticket_tag_ids = [];
|
||||||
|
if(count($ticket->tags) > 0){
|
||||||
|
foreach($ticket->tags as $current_tag_data){
|
||||||
|
$current_ticket_tag_ids[] = $current_tag_data['id'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$this->renderView('views/ticket/edit.html',[
|
$this->renderView('views/ticket/edit.html',[
|
||||||
'ticket' => $ticket,
|
'ticket' => $ticket,
|
||||||
'ticket_meta' => $ticket->getMeta(),
|
'ticket_meta' => $ticket->getMeta(),
|
||||||
'priorities' => $priorities,
|
'priorities' => $priorities,
|
||||||
'statuses' => $statuses,
|
'statuses' => $statuses,
|
||||||
'users' => $users
|
'users' => $users,
|
||||||
|
'all_tags' => $all_tags,
|
||||||
|
'current_ticket_tag_ids' => $current_ticket_tag_ids
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
@ -142,32 +189,40 @@ class TicketController extends BaseController implements CRUD {
|
|||||||
$this->requireLogin();
|
$this->requireLogin();
|
||||||
$this->checkCSRF($f3, '/ticket/create');
|
$this->checkCSRF($f3, '/ticket/create');
|
||||||
|
|
||||||
$ticket_id = $this->f3->get('PARAMS.id');
|
$ticket_id = $f3->get('PARAMS.id');
|
||||||
$ticket_mapper = new Ticket($this->getDB());
|
$ticket_mapper = new Ticket($this->getDB());
|
||||||
$ticket = $ticket_mapper->findById($ticket_id);
|
$ticket = $ticket_mapper->findById($ticket_id);
|
||||||
|
|
||||||
if(!$ticket){
|
if(!$ticket){
|
||||||
$this->f3->set('SESSION.error', 'Ticket not found.');
|
$f3->set('SESSION.error', 'Ticket not found.');
|
||||||
$this->f3->reroute('/tickets');
|
$f3->reroute('/tickets');
|
||||||
}
|
}
|
||||||
|
|
||||||
$data = [
|
$data = [
|
||||||
'title' => $this->f3->get('POST.title'),
|
'title' => $f3->get('POST.title'),
|
||||||
'created_at' => $this->f3->get('POST.created_at'),
|
'created_at' => $f3->get('POST.created_at'),
|
||||||
'description' => $this->f3->get('POST.description'),
|
'description' => $f3->get('POST.description'),
|
||||||
'priority_id' => $this->f3->get('POST.priority_id'),
|
'priority_id' => $f3->get('POST.priority_id'),
|
||||||
'status_id' => $this->f3->get('POST.status_id'),
|
'status_id' => $f3->get('POST.status_id'),
|
||||||
'updated_by' => $this->f3->get('SESSION.user.id') ,
|
'updated_by' => $f3->get('SESSION.user.id') ,
|
||||||
'assigned_to' => $this->f3->get('POST.assigned_to') ?: null
|
'assigned_to' => $f3->get('POST.assigned_to') == -1 ? null : $f3->get('POST.assigned_to')
|
||||||
];
|
];
|
||||||
$ticket->updateTicket($data);
|
$ticket->updateTicket($data);
|
||||||
|
|
||||||
// deal with meta data / custom fields
|
// deal with meta data / custom fields
|
||||||
$meta_keys = $this->f3->get('POST.meta_key');
|
// $meta_keys = $this->f3->get('POST.meta_key');
|
||||||
$meta_values = $this->f3->get('POST.meta_value');
|
// $meta_values = $this->f3->get('POST.meta_value');
|
||||||
$meta_assoc = $ticket->assocMetaFromKeyValue($meta_keys, $meta_values);
|
// $meta_assoc = $ticket->assocMetaFromKeyValue($meta_keys, $meta_values);
|
||||||
$ticket->setCustomFields($meta_assoc);
|
// $ticket->setCustomFields($meta_assoc);
|
||||||
|
|
||||||
|
$posted_tags = $f3->get('POST.tags');
|
||||||
|
if(is_array($posted_tags)){
|
||||||
|
$ticket->setTags($posted_tags);
|
||||||
|
} elseif (empty($posted_tags)){
|
||||||
|
$ticket->setTags([]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$f3->set('SESSION.message', 'Ticket #' . $ticket_id . ' updated successfully.') ;
|
||||||
$f3->reroute('/ticket/' . $ticket_id);
|
$f3->reroute('/ticket/' . $ticket_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -14,7 +14,11 @@ class BulmaFormHelper extends \Prefab {
|
|||||||
static public function render($node) {
|
static public function render($node) {
|
||||||
|
|
||||||
$attr = $node['@attrib'] ?? [];
|
$attr = $node['@attrib'] ?? [];
|
||||||
$type = strtoupper($attr['type']) ?? null;
|
if(isset($attr['type'])){
|
||||||
|
$type = strtoupper($attr['type']);
|
||||||
|
} else {
|
||||||
|
$type = null;
|
||||||
|
}
|
||||||
|
|
||||||
// all *
|
// all *
|
||||||
$label = $attr['label'] ?? '';
|
$label = $attr['label'] ?? '';
|
||||||
|
|||||||
@ -46,8 +46,9 @@ class IconsHelper extends \Prefab {
|
|||||||
|
|
||||||
switch($attr['type']){
|
switch($attr['type']){
|
||||||
case 'status-selector':
|
case 'status-selector':
|
||||||
$selected = Base::instance()->get('GET.status') ?: null;
|
// $selected = Base::instance()->get('GET.status') ?: null;
|
||||||
return '<?php echo \IconsHelper::instance()->renderStatusSelector("'.$selected.'", "'.$path.'"); ?>';
|
return '<?php echo \IconsHelper::instance()->renderStatusSelector(
|
||||||
|
Base::instance()->get("GET.status") ?: null, "'.$path.'"); ?>';
|
||||||
return self::renderStatusSelector($selected, $path);
|
return self::renderStatusSelector($selected, $path);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@ -87,7 +88,7 @@ class IconsHelper extends \Prefab {
|
|||||||
foreach (self::$status_icons as $k => $icon) {
|
foreach (self::$status_icons as $k => $icon) {
|
||||||
$active = ($current_status == $k);
|
$active = ($current_status == $k);
|
||||||
$url = $path . ($active ? '' : '/?status=' . $k);
|
$url = $path . ($active ? '' : '/?status=' . $k);
|
||||||
$class = 'button' . ($active ? ' is-primary' : '');
|
$class = 'button' . ($active ? ' is-inverted' : '');
|
||||||
|
|
||||||
$output .= '<p class="control">';
|
$output .= '<p class="control">';
|
||||||
$output .= '<a href="' . $url . '" class="' . $class . '">';
|
$output .= '<a href="' . $url . '" class="' . $class . '">';
|
||||||
|
|||||||
@ -63,6 +63,7 @@ class Ticket extends \DB\SQL\Mapper {
|
|||||||
|
|
||||||
public function getTagsForTickets(array $tickets)
|
public function getTagsForTickets(array $tickets)
|
||||||
{
|
{
|
||||||
|
if(empty($tickets)) return [];
|
||||||
$tag_mapper = new Tag($this->db, 'ticket');
|
$tag_mapper = new Tag($this->db, 'ticket');
|
||||||
$tickets = $tag_mapper->getTagsFor($tickets);
|
$tickets = $tag_mapper->getTagsFor($tickets);
|
||||||
|
|
||||||
@ -74,8 +75,34 @@ class Ticket extends \DB\SQL\Mapper {
|
|||||||
$this->status_name = 'SELECT name FROM ticket_statuses WHERE tickets.status_id = ticket_statuses.id';
|
$this->status_name = 'SELECT name FROM ticket_statuses WHERE tickets.status_id = ticket_statuses.id';
|
||||||
$this->priority_name = 'SELECT name FROM ticket_priorities WHERE tickets.priority_id = ticket_priorities.id';
|
$this->priority_name = 'SELECT name FROM ticket_priorities WHERE tickets.priority_id = ticket_priorities.id';
|
||||||
$this->load(['id = ?', $id]);
|
$this->load(['id = ?', $id]);
|
||||||
$this->tags = (new Tag($this->db,'ticket'))->getTagsForID($id, 'ticket_id');
|
if($this->dry()){
|
||||||
return $this->dry() ? null : $this;
|
return null;
|
||||||
|
}
|
||||||
|
$tag_model = new Tag($this->db, 'ticket');
|
||||||
|
$this->tags = $tag_model->getTagsForID($this->id, 'ticket_id');
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setTags(array $tags_ids):void {
|
||||||
|
if($this->dry() || !$this->id){
|
||||||
|
// can't set tags for a ticket that hasn't been saved or loaded
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove existing tags - TODO: shouldn't this be in the tag model?
|
||||||
|
$this->db->exec('DELETE FROM ticket_tags WHERE ticket_id = ?', [$this->id]);
|
||||||
|
|
||||||
|
if(!empty($tags_ids)){
|
||||||
|
$sql_insert_tag = 'INSERT INTO ticket_tags (ticket_id, tag_id) VALUES (?,?)';
|
||||||
|
foreach($tags_ids as $tag_id){
|
||||||
|
if(filter_var($tag_id, FILTER_VALIDATE_INT)){
|
||||||
|
$this->db->exec($sql_insert_tag, [$this->id, (int)$tag_id]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// refresh tags
|
||||||
|
$tag_model = new Tag($this->db, 'ticket');
|
||||||
|
$this->tags = $tag_model->getTagsForID($this->id, 'ticket_id');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function createTicket(array $data): int
|
public function createTicket(array $data): int
|
||||||
@ -93,11 +120,15 @@ class Ticket extends \DB\SQL\Mapper {
|
|||||||
$this->updated_at = date('Y-m-d H:i:s');
|
$this->updated_at = date('Y-m-d H:i:s');
|
||||||
|
|
||||||
$this->save();
|
$this->save();
|
||||||
|
$this->logCreate();
|
||||||
return (int)$this->id;
|
return (int)$this->id;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function updateTicket(array $data): void
|
public function updateTicket(array $data): void
|
||||||
{
|
{
|
||||||
|
|
||||||
|
$this->logDiff($data);
|
||||||
|
|
||||||
if(isset($data['title'])){ $this->title = $data['title']; }
|
if(isset($data['title'])){ $this->title = $data['title']; }
|
||||||
if(isset($data['description'])) { $this->description = $data['description']; }
|
if(isset($data['description'])) { $this->description = $data['description']; }
|
||||||
if(isset($data['priority_id'])) { $this->priority_id = $data['priority_id']; }
|
if(isset($data['priority_id'])) { $this->priority_id = $data['priority_id']; }
|
||||||
@ -113,8 +144,6 @@ class Ticket extends \DB\SQL\Mapper {
|
|||||||
$this->created_at = ($data['created_at'] == '' ? date('Y-m-d H:i:s') : $data['created_at']) ?? date('Y-m-d H:i:s');
|
$this->created_at = ($data['created_at'] == '' ? date('Y-m-d H:i:s') : $data['created_at']) ?? date('Y-m-d H:i:s');
|
||||||
$this->updated_at = date('Y-m-d H:i:s');
|
$this->updated_at = date('Y-m-d H:i:s');
|
||||||
|
|
||||||
// printf('<pre>%s</pre>', print_r($this,1)); exit();
|
|
||||||
|
|
||||||
$this->save();
|
$this->save();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -244,5 +273,79 @@ class Ticket extends \DB\SQL\Mapper {
|
|||||||
$user = $this->db->exec($sql, [$this->assigned_to]);
|
$user = $this->db->exec($sql, [$this->assigned_to]);
|
||||||
return $user[0];
|
return $user[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logs a change to the ticket history.
|
||||||
|
* @param int $user_id - the ID of the user making the change.
|
||||||
|
* @param string $field_changed - the name of the field that was changed.
|
||||||
|
* @param string|null $old_value - the old value
|
||||||
|
* @param string|null $new_value - the new value
|
||||||
|
*/
|
||||||
|
public function logHistory(int $user_id, string $field_changed,
|
||||||
|
$old_value = null, $new_value = null,
|
||||||
|
$changed_at = null): void
|
||||||
|
{
|
||||||
|
if($this->dry() || !$this->id) return;
|
||||||
|
|
||||||
|
$history = new \DB\SQL\Mapper($this->db, 'ticket_history');
|
||||||
|
$history->ticket_id = $this->id;
|
||||||
|
$history->user_id = $user_id;
|
||||||
|
$history->field_changed = $field_changed;
|
||||||
|
$history->old_value = $old_value === null ? null : (string)$old_value;
|
||||||
|
$history->new_value = $new_value === null ? null : (string)$new_value;
|
||||||
|
if($changed_at == null){
|
||||||
|
$history->changed_at = date('Y-m-d H:i:s');
|
||||||
|
} else {
|
||||||
|
$history->changed_at = $changed_at;
|
||||||
|
}
|
||||||
|
$history->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getHistory(): array {
|
||||||
|
if($this->dry() || !$this->id) return[];
|
||||||
|
|
||||||
|
$sql = 'SELECT th.*, u.display_name as user_display_name
|
||||||
|
FROM ticket_history th
|
||||||
|
JOIN users u ON th.user_id = u.id
|
||||||
|
WHERE th.ticket_id = ?
|
||||||
|
ORDER BY th.changed_at DESC';
|
||||||
|
return $this->db->exec($sql, [$this->id]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* called from create
|
||||||
|
*/
|
||||||
|
public function logCreate(){
|
||||||
|
$changed_at = date('Y-m-d H:i:s');
|
||||||
|
$user_making_change = \Base::instance()->get('SESSION.user.id');
|
||||||
|
$this->logHistory($user_making_change, 'ticket_created', null, $this->title, $changed_at);
|
||||||
|
if($this->status_id) $this->logHistory($user_making_change, 'status_id', null, $this->status_id, $changed_at);
|
||||||
|
if($this->priority_id) $this->logHistory($user_making_change, 'priority_id', null, $this->priority_id, $changed_at);
|
||||||
|
if($this->assigned_to) $this->logHistory($user_making_change, 'assigned_to', null, $this->assigned_to, $changed_at);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* called from update
|
||||||
|
*/
|
||||||
|
public function logDiff($data){
|
||||||
|
$user_making_change = \Base::instance()->get('SESSION.user.id');
|
||||||
|
$changed_at = date('Y-m-d H:i:s');
|
||||||
|
|
||||||
|
$checks = ['title', 'description', 'priority_id', 'status_id', 'assigned_to', 'updated_by'];
|
||||||
|
|
||||||
|
// loop instead
|
||||||
|
foreach($checks as $check){
|
||||||
|
if(isset($data[$check]) && $this->$check != $data[$check]){
|
||||||
|
if($check == 'description'){
|
||||||
|
$old_hash = hash('sha256', $this->$check);
|
||||||
|
$new_hash = hash('sha256', $data[$check]);
|
||||||
|
$this->logHistory($user_making_change, $check, $old_hash, $new_hash, $changed_at);
|
||||||
|
} else {
|
||||||
|
$this->logHistory($user_making_change, $check, $this->$check, $data[$check], $changed_at);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -54,15 +54,29 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<bulma type="FIELD_SELECT" label="Priority:" name="priority_id"
|
<bulma type="FIELD_SELECT" label="Priority:" name="priority_id" options="{{@priorities}}"
|
||||||
options="{{@priorities}}" option_value="id" option_name="name" selected="0"></bulma>
|
option_value="id" option_name="name" selected="0"></bulma>
|
||||||
|
|
||||||
<bulma type="FIELD_SELECT" label="Status:" name="status_id"
|
<bulma type="FIELD_SELECT" label="Status:" name="status_id" options="{{@statuses}}" option_value="id"
|
||||||
options="{{@statuses}}" option_value="id" option_name="name" selected="0"></bulma>
|
option_name="name" selected="0"></bulma>
|
||||||
|
|
||||||
<bulma type="FIELD_SELECT" label="Assigned User:" name="assigned_to"
|
<bulma type="FIELD_SELECT" label="Assigned User:" name="assigned_to" options="{{@users}}"
|
||||||
options="{{@users}}" option_value="id" option_name="display_name" selected="0"></bulma>
|
option_value="id" option_name="display_name" selected="0"></bulma>
|
||||||
|
|
||||||
|
<!-- field select multiple (TODO: move this to a custom bulma tag)-->
|
||||||
|
<div class="field">
|
||||||
|
<label class="label">Tags</label>
|
||||||
|
<div class="control">
|
||||||
|
<div class="select is-multiple" style="width:100%">
|
||||||
|
<select name="tags[]" multiple size="5" style="width:100%">
|
||||||
|
<repeat group="{{ @all_tags }}" value="{{@tag_option}}">
|
||||||
|
<option value="{{ @tag_option.id}}">{{ @tag_option.name }}</option>
|
||||||
|
</repeat>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<p class="help">Hold Ctrl to select multiple.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -2,93 +2,116 @@
|
|||||||
<!-- made to look more in line with view-->
|
<!-- made to look more in line with view-->
|
||||||
<form action="/ticket/{{ @PARAMS.id }}/update" method="POST">
|
<form action="/ticket/{{ @PARAMS.id }}/update" method="POST">
|
||||||
{{ \CSRFHelper::field() | raw }}
|
{{ \CSRFHelper::field() | raw }}
|
||||||
<div class="is-flex">
|
<div class="is-flex">
|
||||||
<div class="is-flex-grow-1">
|
<div class="is-flex-grow-1">
|
||||||
<bulma type="FIELD_INPUT" name="title" value="{{@ticket.title}}" class="mr-3"></bulma>
|
<bulma type="FIELD_INPUT" name="title" value="{{@ticket.title}}" class="mr-3"></bulma>
|
||||||
</div>
|
|
||||||
<div class="field is-grouped">
|
|
||||||
<div class="control">
|
|
||||||
<a class="button is-secondary" href="/ticket/{{ @ticket.id }}">Cancel</a>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="control">
|
<div class="field is-grouped">
|
||||||
<button class="button is-primary" type="submit">Save</button>
|
<div class="control">
|
||||||
</div>
|
<a class="button is-secondary" href="/ticket/{{ @ticket.id }}">Cancel</a>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<hr>
|
|
||||||
<div class="block">
|
|
||||||
|
|
||||||
<div class="columns">
|
|
||||||
<div class="column is-two-thirds">
|
|
||||||
|
|
||||||
<div class="block">
|
|
||||||
<bulma type="FIELD_INPUT" label="Created At:" name="created_at" value="{{@ticket.created_at}}"></bulma>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div class="control">
|
||||||
|
<button class="button is-primary" type="submit">Save</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="tabs">
|
<hr>
|
||||||
<ul>
|
<div class="block">
|
||||||
<li class="is-active">
|
|
||||||
<a data-tab="write"><span class="icon is-small">
|
|
||||||
<i class="fas fa-pen" aria-hidden="true"></i></span><span>Write</span>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a data-tab="preview"><span class="icon is-small">
|
|
||||||
<i class="fas fa-magnifying-glass" aria-hidden="true"></i></span><span>Preview</span>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="tab-write" class="tab-content block">
|
<div class="columns">
|
||||||
|
<div class="column is-two-thirds">
|
||||||
|
|
||||||
|
<div class="block">
|
||||||
|
<bulma type="FIELD_INPUT" label="Created At:" name="created_at" value="{{@ticket.created_at}}">
|
||||||
|
</bulma>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="tabs">
|
||||||
|
<ul>
|
||||||
|
<li class="is-active">
|
||||||
|
<a data-tab="write"><span class="icon is-small">
|
||||||
|
<i class="fas fa-pen" aria-hidden="true"></i></span><span>Write</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a data-tab="preview"><span class="icon is-small">
|
||||||
|
<i class="fas fa-magnifying-glass"
|
||||||
|
aria-hidden="true"></i></span><span>Preview</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="tab-write" class="tab-content block">
|
||||||
<bulma type="FIELD_TEXTAREA" name="description" value="{{@ticket.description}}" rows="20"></bulma>
|
<bulma type="FIELD_TEXTAREA" name="description" value="{{@ticket.description}}" rows="20"></bulma>
|
||||||
</div>
|
</div>
|
||||||
<div id="tab-preview" class="tab-content content">
|
<div id="tab-preview" class="tab-content content">
|
||||||
<div id="preview-output"></div>
|
<div id="preview-output"></div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="column">
|
|
||||||
<div class="block">
|
|
||||||
<!-- priority and status -->
|
|
||||||
<bulma type="FIELD_SELECT" label="Priority:" name="priority_id"
|
|
||||||
options="{{@priorities}}" option_value="id" option_name="name"
|
|
||||||
selected="{{@ticket.priority_id}}"></bulma>
|
|
||||||
|
|
||||||
<bulma type="FIELD_SELECT" label="Status:" name="status_id"
|
<div class="column">
|
||||||
options="{{@statuses}}" option_value="id" option_name="name"
|
<div class="block">
|
||||||
selected="{{@ticket.status_id}}"></bulma>
|
<!-- priority and status -->
|
||||||
|
<bulma type="FIELD_SELECT" label="Priority:" name="priority_id" options="{{@priorities}}"
|
||||||
|
option_value="id" option_name="name" selected="{{@ticket.priority_id}}"></bulma>
|
||||||
|
|
||||||
<bulma type="FIELD_SELECT" label="Assigned User:" name="assigned_to"
|
<bulma type="FIELD_SELECT" label="Status:" name="status_id" options="{{@statuses}}"
|
||||||
options="{{@users}}" option_value="id" option_name="display_name"
|
option_value="id" option_name="name" selected="{{@ticket.status_id}}"></bulma>
|
||||||
selected="{{@ticket.assigned_to}}"></bulma>
|
|
||||||
|
<bulma type="FIELD_SELECT" label="Assigned User:" name="assigned_to" options="{{@users}}"
|
||||||
|
option_value="id" option_name="display_name" selected="{{@ticket.assigned_to}}"></bulma>
|
||||||
|
|
||||||
|
<!-- tags - need to implement a bulma-->
|
||||||
|
<div class="field">
|
||||||
|
<label class="label">Tags</label>
|
||||||
|
<div class="control">
|
||||||
|
<div class="select is-multiple" style="width:100%;">
|
||||||
|
<select name="tags[]" id="tags" multiple size="8" style="width:100%;">
|
||||||
|
<repeat group="{{ @all_tags }}" value="{{ @tag_option }}">
|
||||||
|
<option value="{{ @tag_option.id}}" {{ in_array(@tag_option.id,
|
||||||
|
@current_ticket_tag_ids) ? 'selected' : '' }}>
|
||||||
|
{{ @tag_option.name }}
|
||||||
|
</option>
|
||||||
|
</repeat>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<p class="help">Hold Ctrl to select multiple tags.</p>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<!-- additional data -->
|
<!-- additional data -->
|
||||||
<div class="block">
|
<div class="block">
|
||||||
</div>
|
</div>
|
||||||
<!-- meta data -->
|
<!-- meta data -->
|
||||||
<div class="block">
|
<div class="block">
|
||||||
<table class="table is-bordered is-fullwidth">
|
<table class="table is-bordered is-fullwidth">
|
||||||
<thead>
|
<thead>
|
||||||
<tr><th class="has-width-100">Property</th><th>Value</th></tr>
|
<tr>
|
||||||
</thead>
|
<th class="has-width-100">Property</th>
|
||||||
<tbody>
|
<th>Value</th>
|
||||||
<repeat group="{{ @ticket }}" key="{{ @key }}" value="{{ @value }}">
|
</tr>
|
||||||
<check if="{{ @key !== 'description'}}">
|
</thead>
|
||||||
<tr><td>{{@key}}</td> <td>{{@value}}</td></tr>
|
<tbody>
|
||||||
</check>
|
<repeat group="{{ @ticket }}" key="{{ @key }}" value="{{ @value }}">
|
||||||
</repeat>
|
<check if="{{ @key !== 'description'}}">
|
||||||
</tbody>
|
<tr>
|
||||||
</table>
|
<td>{{@key}}</td>
|
||||||
</div>
|
<td>{{@value}}</td>
|
||||||
<!-- form to add child ticket relationships -->
|
</tr>
|
||||||
<div class="box">
|
</check>
|
||||||
<h4 class="title is-4">Linked Tickets</h4>
|
</repeat>
|
||||||
<!-- parent -->
|
</tbody>
|
||||||
<?php
|
</table>
|
||||||
|
</div>
|
||||||
|
<!-- form to add child ticket relationships -->
|
||||||
|
<div class="box">
|
||||||
|
<h4 class="title is-4">Linked Tickets</h4>
|
||||||
|
<!-- parent -->
|
||||||
|
<?php
|
||||||
/*
|
/*
|
||||||
<check if="{{ @parent_tickets }}">
|
<check if="{{ @parent_tickets }}">
|
||||||
<div class="block">
|
<div class="block">
|
||||||
@ -126,19 +149,19 @@
|
|||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
*/ ?>
|
*/ ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
</form>
|
||||||
<hr>
|
<hr>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
|
|
||||||
|
|
||||||
<exclude>
|
<exclude>
|
||||||
<include href="views/attachment/index.html"></include>
|
<include href="views/attachment/index.html"></include>
|
||||||
<include href="views/comments/view.html"></include>
|
<include href="views/comments/view.html"></include>
|
||||||
</exclude>
|
</exclude>
|
||||||
|
|
||||||
|
|
||||||
@ -147,4 +170,4 @@
|
|||||||
<div class="block" id="comments"></div>
|
<div class="block" id="comments"></div>
|
||||||
-->
|
-->
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
@ -22,24 +22,9 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="block">
|
<div class="block">
|
||||||
<icons type="status-selector" selected="{{ @GET.status ?: null }}" path="tickets"></icons>25
|
<icons type="status-selector" selected="{{ @GET.status ?: null }}" path="tickets"></icons>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<exclude>
|
|
||||||
<div class="block">
|
|
||||||
<div class="field has-addons">
|
|
||||||
<!-- TODO: move this into a template -->
|
|
||||||
<repeat group="{{ IconsHelper::$status_icons}}" key="{{ @k }}" value="{{ @icon }}">
|
|
||||||
<p class="control">
|
|
||||||
<a href="{{ @PATH }}/?status={{ @k }}" class="button">
|
|
||||||
<span class="icon is-small"><i class="fas fa-{{ @icon[0] }}"></i></span>
|
|
||||||
<span>{{ IconsHelper::$status_names[@k] }}</span>
|
|
||||||
</a>
|
|
||||||
</p>
|
|
||||||
</repeat>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</exclude>
|
|
||||||
<hr>
|
<hr>
|
||||||
|
|
||||||
<div id="ticket_list">
|
<div id="ticket_list">
|
||||||
|
|||||||
@ -101,6 +101,73 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<div class="block" id="ticket-history">
|
||||||
|
<h4 class="title is-4">Ticket History</h4>
|
||||||
|
<check if="{{ !empty(@ticket_history) }}">
|
||||||
|
<div class="list is-hoverable">
|
||||||
|
<repeat group="{{ @ticket_history }}" value="{{ @entry }}">
|
||||||
|
<div class="list-item">
|
||||||
|
<div class="list-item-content">
|
||||||
|
<div class="list-item-title">
|
||||||
|
<small class="has-text-grey">
|
||||||
|
{{ date('Y-m-d H:i:s', strtotime(@entry.changed_at)) }} by {{ @entry.user_display_name }}
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
<div class="list-item-description">
|
||||||
|
<!-- creation -->
|
||||||
|
<check if="{{ @entry.field_changed == 'ticket_created' }}">
|
||||||
|
Ticket created: {{ @entry.new_value}}
|
||||||
|
</check>
|
||||||
|
<!-- status -->
|
||||||
|
<check if="{{ @entry.field_changed == 'status_id' }}">
|
||||||
|
Status changed
|
||||||
|
<check if="{{ @entry.old_value !== null }}">
|
||||||
|
from <strong>{{ @map['status'][@entry.old_value] ?? 'Unknown' }}</strong>
|
||||||
|
</check>
|
||||||
|
to <strong>{{ @map['status'][@entry.new_value] ?? 'Unknown' }}</strong>
|
||||||
|
</check>
|
||||||
|
<!-- priority -->
|
||||||
|
<check if="{{ @entry.field_changed == 'priority_id' }}">
|
||||||
|
Priority changed
|
||||||
|
<check if="{{ @entry.old_value !== null }}">
|
||||||
|
from <strong>{{ @map['priorities'][@entry.old_value] ?? 'Unknown'}}</strong>
|
||||||
|
</check>
|
||||||
|
to <strong>{{ @map['priorities'][@entry.new_value] ?? 'Unknown' }}</strong>
|
||||||
|
</check>
|
||||||
|
<!-- assignment -->
|
||||||
|
<check if="{{ @entry.field_changed == 'assigned_to' }}">
|
||||||
|
Assignment changed
|
||||||
|
<check if="{{ @entry.old_value !== null && @entry.old_value != 0 }}">
|
||||||
|
from <strong>{{ @map['users'][@entry.old_value] ?? 'Unassigned' }}</strong>
|
||||||
|
</check>
|
||||||
|
<check if="{{ @entry.old_value === null || @entry.old_value == 0}}">
|
||||||
|
from <strong>Unassigned</strong>
|
||||||
|
</check>
|
||||||
|
to
|
||||||
|
<check if="{{ @entry.new_value !== null && @entry.new_value != 0}}">
|
||||||
|
<strong>{{ @map['users'][@entry.new_value] ?? 'Unassigned' }}</strong>
|
||||||
|
</check>
|
||||||
|
<check if="{{ @entry.new_value === null || @entry.new_value == 0}}">
|
||||||
|
<strong>Unassigned</strong>
|
||||||
|
</check>
|
||||||
|
</check>
|
||||||
|
<check if="{{ @entry.field_changed == 'title'}}">
|
||||||
|
Title changed from "{{ @entry.old_value}}" to "{{ @entry.new_value}}".
|
||||||
|
</check>
|
||||||
|
<check if="{{ @entry.field_changed == 'description'}}">
|
||||||
|
Description updated old sha256({{@entry.old_value}}).
|
||||||
|
</check>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</repeat>
|
||||||
|
</div>
|
||||||
|
<false>
|
||||||
|
<p>No history entries for this ticket.</p>
|
||||||
|
</false>
|
||||||
|
</check>
|
||||||
<!--
|
<!--
|
||||||
<div class="block" id="attachments"></div>
|
<div class="block" id="attachments"></div>
|
||||||
<div class="block" id="comments"></div>
|
<div class="block" id="comments"></div>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user