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_mapper = new Ticket($this->getDB());
|
||||
$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();
|
||||
$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
|
||||
$this->renderView('views/ticket/view.html', [
|
||||
@ -46,7 +59,13 @@ class TicketController extends BaseController implements CRUD {
|
||||
'comments' => $ticket->comments(),
|
||||
'parent_tickets' => $ticket->getParentTickets(),
|
||||
'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();
|
||||
$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?
|
||||
$users = $this->getDB()->exec('SELECT id, username, display_name FROM users ORDER BY display_name ASC');
|
||||
$users = array_merge([['id'=>'-1', 'display_name'=>'--']], $users);
|
||||
@ -66,7 +88,8 @@ class TicketController extends BaseController implements CRUD {
|
||||
$this->renderView('views/ticket/create.html',[
|
||||
'priorities' => $priorities,
|
||||
'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'),
|
||||
'priority_id' => $this->f3->get('POST.priority_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());
|
||||
$new_ticket_id = $ticket_mapper->createTicket($data);
|
||||
|
||||
// custom field
|
||||
$meta_keys = $this->f3->get('POST.meta_key');
|
||||
$meta_values = $this->f3->get('POST.meta_value');
|
||||
$meta_assoc = $ticket_mapper->assocMetaFromKeyValue($meta_keys, $meta_values);
|
||||
$ticket_mapper->setCustomFields($meta_assoc);
|
||||
// $meta_keys = $this->f3->get('POST.meta_key');
|
||||
// $meta_values = $this->f3->get('POST.meta_value');
|
||||
// $meta_assoc = $ticket_mapper->assocMetaFromKeyValue($meta_keys, $meta_values);
|
||||
// $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);
|
||||
}
|
||||
|
||||
@ -120,16 +154,29 @@ class TicketController extends BaseController implements CRUD {
|
||||
// dropdowns
|
||||
$priorities = (new TicketPriority($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?
|
||||
$users = $this->getDB()->exec('SELECT id, username, display_name FROM users ORDER BY display_name ASC');
|
||||
$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',[
|
||||
'ticket' => $ticket,
|
||||
'ticket_meta' => $ticket->getMeta(),
|
||||
'priorities' => $priorities,
|
||||
'statuses' => $statuses,
|
||||
'users' => $users
|
||||
'users' => $users,
|
||||
'all_tags' => $all_tags,
|
||||
'current_ticket_tag_ids' => $current_ticket_tag_ids
|
||||
]
|
||||
);
|
||||
return;
|
||||
@ -142,32 +189,40 @@ class TicketController extends BaseController implements CRUD {
|
||||
$this->requireLogin();
|
||||
$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 = $ticket_mapper->findById($ticket_id);
|
||||
|
||||
if(!$ticket){
|
||||
$this->f3->set('SESSION.error', 'Ticket not found.');
|
||||
$this->f3->reroute('/tickets');
|
||||
$f3->set('SESSION.error', 'Ticket not found.');
|
||||
$f3->reroute('/tickets');
|
||||
}
|
||||
|
||||
$data = [
|
||||
'title' => $this->f3->get('POST.title'),
|
||||
'created_at' => $this->f3->get('POST.created_at'),
|
||||
'description' => $this->f3->get('POST.description'),
|
||||
'priority_id' => $this->f3->get('POST.priority_id'),
|
||||
'status_id' => $this->f3->get('POST.status_id'),
|
||||
'updated_by' => $this->f3->get('SESSION.user.id') ,
|
||||
'assigned_to' => $this->f3->get('POST.assigned_to') ?: null
|
||||
'title' => $f3->get('POST.title'),
|
||||
'created_at' => $f3->get('POST.created_at'),
|
||||
'description' => $f3->get('POST.description'),
|
||||
'priority_id' => $f3->get('POST.priority_id'),
|
||||
'status_id' => $f3->get('POST.status_id'),
|
||||
'updated_by' => $f3->get('SESSION.user.id') ,
|
||||
'assigned_to' => $f3->get('POST.assigned_to') == -1 ? null : $f3->get('POST.assigned_to')
|
||||
];
|
||||
$ticket->updateTicket($data);
|
||||
|
||||
// deal with meta data / custom fields
|
||||
$meta_keys = $this->f3->get('POST.meta_key');
|
||||
$meta_values = $this->f3->get('POST.meta_value');
|
||||
$meta_assoc = $ticket->assocMetaFromKeyValue($meta_keys, $meta_values);
|
||||
$ticket->setCustomFields($meta_assoc);
|
||||
// $meta_keys = $this->f3->get('POST.meta_key');
|
||||
// $meta_values = $this->f3->get('POST.meta_value');
|
||||
// $meta_assoc = $ticket->assocMetaFromKeyValue($meta_keys, $meta_values);
|
||||
// $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);
|
||||
}
|
||||
|
||||
|
||||
@ -14,7 +14,11 @@ class BulmaFormHelper extends \Prefab {
|
||||
static public function render($node) {
|
||||
|
||||
$attr = $node['@attrib'] ?? [];
|
||||
$type = strtoupper($attr['type']) ?? null;
|
||||
if(isset($attr['type'])){
|
||||
$type = strtoupper($attr['type']);
|
||||
} else {
|
||||
$type = null;
|
||||
}
|
||||
|
||||
// all *
|
||||
$label = $attr['label'] ?? '';
|
||||
|
||||
@ -46,8 +46,9 @@ class IconsHelper extends \Prefab {
|
||||
|
||||
switch($attr['type']){
|
||||
case 'status-selector':
|
||||
$selected = Base::instance()->get('GET.status') ?: null;
|
||||
return '<?php echo \IconsHelper::instance()->renderStatusSelector("'.$selected.'", "'.$path.'"); ?>';
|
||||
// $selected = Base::instance()->get('GET.status') ?: null;
|
||||
return '<?php echo \IconsHelper::instance()->renderStatusSelector(
|
||||
Base::instance()->get("GET.status") ?: null, "'.$path.'"); ?>';
|
||||
return self::renderStatusSelector($selected, $path);
|
||||
|
||||
default:
|
||||
@ -87,7 +88,7 @@ class IconsHelper extends \Prefab {
|
||||
foreach (self::$status_icons as $k => $icon) {
|
||||
$active = ($current_status == $k);
|
||||
$url = $path . ($active ? '' : '/?status=' . $k);
|
||||
$class = 'button' . ($active ? ' is-primary' : '');
|
||||
$class = 'button' . ($active ? ' is-inverted' : '');
|
||||
|
||||
$output .= '<p class="control">';
|
||||
$output .= '<a href="' . $url . '" class="' . $class . '">';
|
||||
|
||||
@ -63,6 +63,7 @@ class Ticket extends \DB\SQL\Mapper {
|
||||
|
||||
public function getTagsForTickets(array $tickets)
|
||||
{
|
||||
if(empty($tickets)) return [];
|
||||
$tag_mapper = new Tag($this->db, 'ticket');
|
||||
$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->priority_name = 'SELECT name FROM ticket_priorities WHERE tickets.priority_id = ticket_priorities.id';
|
||||
$this->load(['id = ?', $id]);
|
||||
$this->tags = (new Tag($this->db,'ticket'))->getTagsForID($id, 'ticket_id');
|
||||
return $this->dry() ? null : $this;
|
||||
if($this->dry()){
|
||||
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
|
||||
@ -93,11 +120,15 @@ class Ticket extends \DB\SQL\Mapper {
|
||||
$this->updated_at = date('Y-m-d H:i:s');
|
||||
|
||||
$this->save();
|
||||
$this->logCreate();
|
||||
return (int)$this->id;
|
||||
}
|
||||
|
||||
public function updateTicket(array $data): void
|
||||
{
|
||||
|
||||
$this->logDiff($data);
|
||||
|
||||
if(isset($data['title'])){ $this->title = $data['title']; }
|
||||
if(isset($data['description'])) { $this->description = $data['description']; }
|
||||
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->updated_at = date('Y-m-d H:i:s');
|
||||
|
||||
// printf('<pre>%s</pre>', print_r($this,1)); exit();
|
||||
|
||||
$this->save();
|
||||
}
|
||||
|
||||
@ -245,4 +274,78 @@ class Ticket extends \DB\SQL\Mapper {
|
||||
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"
|
||||
options="{{@priorities}}" option_value="id" option_name="name" selected="0"></bulma>
|
||||
<bulma type="FIELD_SELECT" label="Priority:" name="priority_id" options="{{@priorities}}"
|
||||
option_value="id" option_name="name" selected="0"></bulma>
|
||||
|
||||
<bulma type="FIELD_SELECT" label="Status:" name="status_id"
|
||||
options="{{@statuses}}" option_value="id" option_name="name" selected="0"></bulma>
|
||||
<bulma type="FIELD_SELECT" label="Status:" name="status_id" options="{{@statuses}}" option_value="id"
|
||||
option_name="name" selected="0"></bulma>
|
||||
|
||||
<bulma type="FIELD_SELECT" label="Assigned User:" name="assigned_to"
|
||||
options="{{@users}}" option_value="id" option_name="display_name" selected="0"></bulma>
|
||||
<bulma type="FIELD_SELECT" label="Assigned User:" name="assigned_to" options="{{@users}}"
|
||||
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>
|
||||
|
||||
@ -2,93 +2,116 @@
|
||||
<!-- made to look more in line with view-->
|
||||
<form action="/ticket/{{ @PARAMS.id }}/update" method="POST">
|
||||
{{ \CSRFHelper::field() | raw }}
|
||||
<div class="is-flex">
|
||||
<div class="is-flex-grow-1">
|
||||
<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 class="is-flex">
|
||||
<div class="is-flex-grow-1">
|
||||
<bulma type="FIELD_INPUT" name="title" value="{{@ticket.title}}" class="mr-3"></bulma>
|
||||
</div>
|
||||
<div class="control">
|
||||
<button class="button is-primary" type="submit">Save</button>
|
||||
</div>
|
||||
</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 class="field is-grouped">
|
||||
<div class="control">
|
||||
<a class="button is-secondary" href="/ticket/{{ @ticket.id }}">Cancel</a>
|
||||
</div>
|
||||
<div class="control">
|
||||
<button class="button is-primary" type="submit">Save</button>
|
||||
</div>
|
||||
</div>
|
||||
</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>
|
||||
<hr>
|
||||
<div class="block">
|
||||
|
||||
<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>
|
||||
</div>
|
||||
<div id="tab-preview" class="tab-content content">
|
||||
</div>
|
||||
<div id="tab-preview" class="tab-content content">
|
||||
<div id="preview-output"></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>
|
||||
<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"
|
||||
options="{{@statuses}}" option_value="id" option_name="name"
|
||||
selected="{{@ticket.status_id}}"></bulma>
|
||||
<bulma type="FIELD_SELECT" label="Status:" name="status_id" options="{{@statuses}}"
|
||||
option_value="id" option_name="name" selected="{{@ticket.status_id}}"></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>
|
||||
<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>
|
||||
<!-- additional data -->
|
||||
<div class="block">
|
||||
</div>
|
||||
<!-- meta data -->
|
||||
<div class="block">
|
||||
<table class="table is-bordered is-fullwidth">
|
||||
<thead>
|
||||
<tr><th class="has-width-100">Property</th><th>Value</th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<repeat group="{{ @ticket }}" key="{{ @key }}" value="{{ @value }}">
|
||||
<check if="{{ @key !== 'description'}}">
|
||||
<tr><td>{{@key}}</td> <td>{{@value}}</td></tr>
|
||||
</check>
|
||||
</repeat>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!-- form to add child ticket relationships -->
|
||||
<div class="box">
|
||||
<h4 class="title is-4">Linked Tickets</h4>
|
||||
<!-- parent -->
|
||||
<?php
|
||||
</div>
|
||||
<!-- additional data -->
|
||||
<div class="block">
|
||||
</div>
|
||||
<!-- meta data -->
|
||||
<div class="block">
|
||||
<table class="table is-bordered is-fullwidth">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="has-width-100">Property</th>
|
||||
<th>Value</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<repeat group="{{ @ticket }}" key="{{ @key }}" value="{{ @value }}">
|
||||
<check if="{{ @key !== 'description'}}">
|
||||
<tr>
|
||||
<td>{{@key}}</td>
|
||||
<td>{{@value}}</td>
|
||||
</tr>
|
||||
</check>
|
||||
</repeat>
|
||||
</tbody>
|
||||
</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 }}">
|
||||
<div class="block">
|
||||
@ -126,10 +149,10 @@
|
||||
</div>
|
||||
</form>
|
||||
*/ ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<hr>
|
||||
|
||||
@ -137,8 +160,8 @@
|
||||
|
||||
|
||||
<exclude>
|
||||
<include href="views/attachment/index.html"></include>
|
||||
<include href="views/comments/view.html"></include>
|
||||
<include href="views/attachment/index.html"></include>
|
||||
<include href="views/comments/view.html"></include>
|
||||
</exclude>
|
||||
|
||||
|
||||
|
||||
@ -22,24 +22,9 @@
|
||||
</div>
|
||||
|
||||
<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>
|
||||
|
||||
<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>
|
||||
|
||||
<div id="ticket_list">
|
||||
|
||||
@ -101,6 +101,73 @@
|
||||
</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="comments"></div>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user