refactored tickets to include tags
This commit is contained in:
parent
b13cd3448f
commit
c0f9ed1094
@ -9,9 +9,16 @@ class TicketController extends BaseController implements CRUD {
|
|||||||
|
|
||||||
$this->requireLogin();
|
$this->requireLogin();
|
||||||
|
|
||||||
|
$filter = $f3->get('GET.status');
|
||||||
|
|
||||||
// retrieve tickets
|
// retrieve tickets
|
||||||
$ticket_mapper = new Ticket($this->getDB());
|
$ticket_mapper = new Ticket($this->getDB());
|
||||||
|
|
||||||
|
if($filter){
|
||||||
|
$tickets = $ticket_mapper->findFiltered($filter);
|
||||||
|
} else {
|
||||||
$tickets = $ticket_mapper->findAll();
|
$tickets = $ticket_mapper->findAll();
|
||||||
|
}
|
||||||
|
|
||||||
// render
|
// render
|
||||||
$this->renderView('../ui/views/ticket/index.html',
|
$this->renderView('../ui/views/ticket/index.html',
|
||||||
|
|||||||
79
app/models/Tag.php
Normal file
79
app/models/Tag.php
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
class Tag extends \DB\SQL\Mapper
|
||||||
|
{
|
||||||
|
|
||||||
|
protected $tag_table, $tag_table_id;
|
||||||
|
|
||||||
|
function __construct($db, $type = null)
|
||||||
|
{
|
||||||
|
if($type == null){
|
||||||
|
// do tag mapping
|
||||||
|
parent::__construct($db, 'tags');
|
||||||
|
} else {
|
||||||
|
$this->tag_table = $type . '_tags';
|
||||||
|
$this->tag_table_id = $type . '_id';
|
||||||
|
parent::__construct($db, $this->tag_table);
|
||||||
|
}
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// VERIFY: possible issue with this?
|
||||||
|
public function getTagsFor($objects, $id_key = 'id')
|
||||||
|
{
|
||||||
|
// echo $this->get('_type_id'); exit;
|
||||||
|
// printf('<pre>%s</pre>', print_r($this,1)); exit;
|
||||||
|
if(empty($objects)) return [];
|
||||||
|
$ids = array_column($objects, $id_key);
|
||||||
|
$placeholders = implode(',', array_fill(0, count($ids), '?'));
|
||||||
|
$sql =
|
||||||
|
'SELECT tt.%1$s, t.id, t.name, t.color
|
||||||
|
FROM %2$s tt
|
||||||
|
INNER JOIN tags t ON tt.tag_id = t.id
|
||||||
|
WHERE tt.%1$s IN (%3$s)';
|
||||||
|
$sql_sprintf = sprintf($sql, $this->tag_table_id, $this->tag_table, $placeholders);
|
||||||
|
$rows = $this->db->exec($sql_sprintf, $ids);
|
||||||
|
|
||||||
|
$tags_map = [];
|
||||||
|
foreach($rows as $row)
|
||||||
|
{
|
||||||
|
$tags_map[$row[$this->tag_table_id]][] = $row;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach($objects as &$object)
|
||||||
|
{
|
||||||
|
$object['tags'] = $tags_map[$object[$id_key]] ?? [];
|
||||||
|
}
|
||||||
|
return $objects;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTagsForID($id, $id_key = 'id')
|
||||||
|
{
|
||||||
|
$sql = 'SELECT tt.%1$s, t.id, t.name, t.color
|
||||||
|
FROM %2$s tt
|
||||||
|
INNER JOIN tags t ON tt.tag_id = t.id
|
||||||
|
WHERE tt.%1$s = ?';
|
||||||
|
$sql_sprintf = sprintf($sql, $this->tag_table_id, $this->tag_table);
|
||||||
|
$rows = $this->db->exec($sql_sprintf, $id);
|
||||||
|
return $rows;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function findLinkedTags($id = '')
|
||||||
|
{
|
||||||
|
$sql = '
|
||||||
|
SELECT t.name, t.color
|
||||||
|
FROM `?` tt
|
||||||
|
LEFT JOIN `tags` t ON t.id = tt.id
|
||||||
|
WHERE tt.`?` = ?
|
||||||
|
';
|
||||||
|
$params = [
|
||||||
|
$this->_type,
|
||||||
|
$this->_type_id,
|
||||||
|
$id
|
||||||
|
];
|
||||||
|
return $this->db->exec($sql, $params);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@ -3,6 +3,7 @@
|
|||||||
class Ticket extends \DB\SQL\Mapper {
|
class Ticket extends \DB\SQL\Mapper {
|
||||||
function __construct($db){
|
function __construct($db){
|
||||||
parent::__construct($db, 'tickets');
|
parent::__construct($db, 'tickets');
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -10,8 +11,9 @@ class Ticket extends \DB\SQL\Mapper {
|
|||||||
*/
|
*/
|
||||||
public function findAll(): array
|
public function findAll(): array
|
||||||
{
|
{
|
||||||
return $this->db->exec(
|
$tickets = $this->db->exec(
|
||||||
'SELECT t.* , tp.name AS priority_name, ts.name AS status_name, u.display_name
|
'SELECT t.id, t.title, t.created_at,
|
||||||
|
tp.name AS priority_name, ts.name AS status_name, u.display_name
|
||||||
FROM tickets t
|
FROM tickets t
|
||||||
LEFT JOIN ticket_priorities tp ON t.priority_id = tp.id
|
LEFT JOIN ticket_priorities tp ON t.priority_id = tp.id
|
||||||
LEFT JOIN ticket_statuses ts ON t.status_id = ts.id
|
LEFT JOIN ticket_statuses ts ON t.status_id = ts.id
|
||||||
@ -19,11 +21,60 @@ class Ticket extends \DB\SQL\Mapper {
|
|||||||
WHERE t.recycled = 0
|
WHERE t.recycled = 0
|
||||||
ORDER BY t.created_at DESC'
|
ORDER BY t.created_at DESC'
|
||||||
);
|
);
|
||||||
|
$result = $this->getTagsForTickets($tickets);
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function findFiltered(string $filter): array
|
||||||
|
{
|
||||||
|
$sql = '
|
||||||
|
SELECT t.*, tp.name AS priority_name, ts.name AS status_name, u.display_name
|
||||||
|
FROM tickets t
|
||||||
|
LEFT JOIN ticket_priorities tp ON t.priority_id = tp.id
|
||||||
|
LEFT JOIN ticket_statuses ts ON t.status_id = ts.id
|
||||||
|
LEFT JOIN users u ON t.created_by = u.id
|
||||||
|
WHERE t.recycled = 0
|
||||||
|
';
|
||||||
|
$params = [];
|
||||||
|
switch($filter){
|
||||||
|
case 'open':
|
||||||
|
$sql .= ' AND status_id = ?';
|
||||||
|
$params[] = 1;
|
||||||
|
break;
|
||||||
|
case 'in_progress':
|
||||||
|
$sql .= ' AND status_id = ?';
|
||||||
|
$params[] = 2;
|
||||||
|
break;
|
||||||
|
case 'on_hold':
|
||||||
|
$sql .= ' AND status_id = ?';
|
||||||
|
$params[] = 3;
|
||||||
|
break;
|
||||||
|
case 'completed':
|
||||||
|
$sql .= ' AND status_id = ?';
|
||||||
|
$params[] = 4;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
$sql .= ' ORDER BY t.created_at DESC';
|
||||||
|
$tickets = $this->db->exec($sql, $params);
|
||||||
|
$result = $this->getTagsForTickets($tickets);
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTagsForTickets(array $tickets)
|
||||||
|
{
|
||||||
|
$tag_mapper = new Tag($this->db, 'ticket');
|
||||||
|
$tickets = $tag_mapper->getTagsFor($tickets);
|
||||||
|
|
||||||
|
return $tickets;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function findById($id): ?Ticket
|
public function findById($id): ?Ticket
|
||||||
{
|
{
|
||||||
|
$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->load(['id = ?', $id]);
|
||||||
|
$this->tags = (new Tag($this->db,'ticket'))->getTagsForID($id, 'ticket_id');
|
||||||
return $this->dry() ? null : $this;
|
return $this->dry() ? null : $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -35,7 +86,7 @@ class Ticket extends \DB\SQL\Mapper {
|
|||||||
$this->description = $data['description'] ?? '';
|
$this->description = $data['description'] ?? '';
|
||||||
//
|
//
|
||||||
$this->priority_id = $data['priority_id'] ?? null;
|
$this->priority_id = $data['priority_id'] ?? null;
|
||||||
$this->status = $data['status_id'] ?? null;
|
$this->status_id = $data['status_id'] ?? null;
|
||||||
//
|
//
|
||||||
$this->created_by = $data['created_by'] ?? null;
|
$this->created_by = $data['created_by'] ?? null;
|
||||||
$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');
|
||||||
|
|||||||
2
public/css/main.min.css
vendored
2
public/css/main.min.css
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -28,12 +28,13 @@
|
|||||||
@extend .is-flex;
|
@extend .is-flex;
|
||||||
@extend .is-justify-content-flex-start;
|
@extend .is-justify-content-flex-start;
|
||||||
@extend .is-flex-wrap-wrap;
|
@extend .is-flex-wrap-wrap;
|
||||||
|
@extend .mb-1;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
.ticket-title {
|
.ticket-title {
|
||||||
@extend .title;
|
@extend .title;
|
||||||
|
@extend .mb-0;
|
||||||
@extend .is-5;
|
@extend .is-5;
|
||||||
@extend .mb-1;
|
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -15,16 +15,10 @@
|
|||||||
<span class="ticket-title">
|
<span class="ticket-title">
|
||||||
<a href="/ticket/{{ @ticket.id }}">{{ @ticket.title }}</a>
|
<a href="/ticket/{{ @ticket.id }}">{{ @ticket.title }}</a>
|
||||||
</span>
|
</span>
|
||||||
<div class="tags">
|
|
||||||
<div class="tags ml-2">
|
<div class="tags ml-2">
|
||||||
<!-- TODO: get tags -->
|
|
||||||
<span class="tag is-link">tag</span>
|
|
||||||
</div>
|
|
||||||
<?php /*
|
|
||||||
<repeat group="{{ @ticket.tags }}" value="{{ @tag }}">
|
<repeat group="{{ @ticket.tags }}" value="{{ @tag }}">
|
||||||
<span class="tag is-link">{{ @tag }}</span>
|
<span class="tag is-{{@tag.color}}">{{ @tag.name }}</span>
|
||||||
</repeat>
|
</repeat>
|
||||||
*/ ?>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="ticket-meta">
|
<div class="ticket-meta">
|
||||||
|
|||||||
@ -6,7 +6,7 @@
|
|||||||
<bulma type="H_FIELD_INPUT" label="Created At:" name="created_at" value=""></bulma>
|
<bulma type="H_FIELD_INPUT" label="Created At:" name="created_at" value=""></bulma>
|
||||||
<bulma type="H_FIELD_TEXTAREA" label="Description:" name="description" value=""></bulma>
|
<bulma type="H_FIELD_TEXTAREA" label="Description:" name="description" value=""></bulma>
|
||||||
|
|
||||||
<!-- TODO implement priority and status -->
|
<!-- priority and status -->
|
||||||
<bulma type="H_FIELD_SELECT_NEW" label="Priority:" name="priority_id"
|
<bulma type="H_FIELD_SELECT_NEW" label="Priority:" name="priority_id"
|
||||||
options="priorities" option_value="id" option_name="name"
|
options="priorities" option_value="id" option_name="name"
|
||||||
selected="2"></bulma>
|
selected="2"></bulma>
|
||||||
|
|||||||
@ -51,6 +51,16 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="column">
|
<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>
|
||||||
|
</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">
|
||||||
|
|||||||
@ -13,7 +13,7 @@
|
|||||||
|
|
||||||
<bulma type="H_FIELD_SELECT_NEW" label="Status:" name="status_id"
|
<bulma type="H_FIELD_SELECT_NEW" label="Status:" name="status_id"
|
||||||
options="statuses" option_value="id" option_name="name"
|
options="statuses" option_value="id" option_name="name"
|
||||||
selected="@ticket.status_id"></bulma>
|
selected="{{@ticket.status_id}}"></bulma>
|
||||||
|
|
||||||
<include href="/ui/parts/clipboard.html"></include>
|
<include href="/ui/parts/clipboard.html"></include>
|
||||||
|
|
||||||
|
|||||||
@ -2,6 +2,7 @@
|
|||||||
<include href="/ui/session/error.html"></include>
|
<include href="/ui/session/error.html"></include>
|
||||||
</include>
|
</include>
|
||||||
<!-- updated design -- inspiration gitea -->
|
<!-- updated design -- inspiration gitea -->
|
||||||
|
<div class="block">
|
||||||
<div class="field is-grouped">
|
<div class="field is-grouped">
|
||||||
<div class="control is-expanded">
|
<div class="control is-expanded">
|
||||||
<div class="field has-addons is-expanded">
|
<div class="field has-addons is-expanded">
|
||||||
@ -9,7 +10,8 @@
|
|||||||
<input class="input" type="text" placeholder="Find a ticket">
|
<input class="input" type="text" placeholder="Find a ticket">
|
||||||
</div>
|
</div>
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<button class="button is-info"><span class="icon"><i class="fas fa-magnifying-glass"></i></span></button>
|
<button class="button is-info"><span class="icon"><i
|
||||||
|
class="fas fa-magnifying-glass"></i></span></button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -19,6 +21,20 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
<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>
|
||||||
<hr>
|
<hr>
|
||||||
|
|
||||||
<div id="ticket_list">
|
<div id="ticket_list">
|
||||||
|
|||||||
@ -24,6 +24,11 @@
|
|||||||
<div class="column">
|
<div class="column">
|
||||||
<!-- meta data -->
|
<!-- meta data -->
|
||||||
<div class="block">
|
<div class="block">
|
||||||
|
<div class="tags">
|
||||||
|
<repeat group="{{ @ticket.tags }}" value="{{@tag}}">
|
||||||
|
<span class="tag is-{{@tag.color}}">{{@tag.name}}</span>
|
||||||
|
</repeat>
|
||||||
|
</div>
|
||||||
<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><th class="has-width-100">Property</th><th>Value</th></tr>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user