Compare commits

..

No commits in common. "7083a55e0333d717f0e25f79b740e79753a00105" and "bd9ddfb0d2da552876686695f9b63ee3d6486a49" have entirely different histories.

24 changed files with 46 additions and 21928 deletions

5
.gitignore vendored
View File

@ -1,11 +1,8 @@
composer.lock composer.lock
package-lock.json
lib/ lib/
downloads/ downloads/
public/tmp/ public/tmp/
storage/ storage/
public/test.php public/test.php
app/config/.env.cfg app/config/.env.cfg
app/.env.cfg app/.env.cfg
app/config/tp_servicedesk.sql
node_modules

View File

@ -60,23 +60,4 @@ POST /project/@id/update=ProjectController->update
; additional routes - user ; additional routes - user
GET /users=UserController->index GET /users=UserController->index
GET /user/@id/edit=UserController->editForm GET /user/@id/edit=UserController->editForm
POST /user/@id/update=UserController->update POST /user/@id/update=UserController->update
; admin
GET /admin=Admin\HomeController->index
; admin/priority
GET /admin/priority=Admin\TicketOptionsController->listPriorities
GET /admin/priority/create=Admin\TicketOptionsController->createPriorityForm
POST /admin/priority/create=Admin\TicketOptionsController->createPriority
GET /admin/priority/@id/edit=Admin\TicketController->editPriorityForm
POST /admin/priority/@id/update=Admin\TicketController->updatePriority
GET /admin/priority/@id/delete=Admin\TicketController->deletePriority
; admin/status
GET /admin/status=Admin\TicketOptionsController->listStatuses
GET /admin/status/create=Admin\TicketOptionsController->createStatusForm
POST /admin/status/create=Admin\TicketOptionsController->createStatus
GET /admin/status/@id/edit=Admin\TicketController->editStatusForm
POST /admin/status/@id/update=Admin\TicketController->updateStatus
GET /admin/status/@id/delete=Admin\TicketController->deleteStatus

View File

@ -1,11 +0,0 @@
<?php
namespace Admin;
class HomeController extends \BaseController
{
public function index($f3)
{
$this->renderView('/ui/views/admin/index.html');
}
}

View File

@ -1,36 +0,0 @@
<?php
namespace Admin;
class TicketOptionsController extends \BaseController
{
public function listPriorities()
{
$this->requireLogin();
// TODO: check admin
$model = new \TicketPriority($this->getDB());
$priorities = $model->findAll();
$this->renderView('/ui/views/admin/priorities/index.html', [
'priorities' => $priorities
]);
}
public function createPriorityForm()
{
$this->requireLogin();
$this->renderView('/ui/views/admin/priorities/create.html');
}
public function createPriority()
{
$this->requireLogin();
$p = new \TicketPriority($this->getDB());
$p->name = $this->f3->get('POST.name');
$p->sort_order = $this->f3->get('POST.sort_order');
$p->save();
}
// TODO: editPriorityForm(), updatePriorityForm(), deletePriorityForm()
}

View File

@ -3,23 +3,16 @@
class IconsHelper extends \Prefab { class IconsHelper extends \Prefab {
static public $status_icons = [ static public $status_icons = [
'New' => ['fas fa-circle-dot has-text-success', "🆕"], 'New' => ['fas fa-plus-circle has-text-warning', "🆕"],
'In Progress' => ['fas fa-circle-play has-text-link', "🔄"], 'In Progress' => ['fas fa-repeat has-text-link', "🔄"],
'On Hold' => ['fas fa-pause-circle has-text-warning',"⏸️"], 'On Hold' => ['fas fa-pause-circle has-text-danger',"⏸️"],
'Completed' => ['fas fa-check-circle has-text-danger', ""] 'Completed' => ['fas fa-check-circle has-text-success', ""]
]; ];
static public $priority_icons = [ static public $priority_icons = [
'Low' => ['fas fa-circle-down',"🟢"], 'Low' => ['fas fa-arrow-down',"🟢"],
'Medium' => ['fas fa-circle-dot', "🟡"], 'Medium' => ['fas fa-minus', "🟡"],
'High' => ['fas fa-circle-up', "🔴"] 'High' => ['fas fa-arrow-up', "🔴"]
];
static public $priority_colors = [
'Low' => 'success',
'Medium' => 'warning',
'High' => 'danger',
'' => 'info'
]; ];
static public function icons($node){ static public function icons($node){
@ -45,21 +38,13 @@ class IconsHelper extends \Prefab {
break; break;
case 'priority': case 'priority':
$icon_class = IconsHelper::$priority_icons[$value] ?? ['fas fa-question-circle', "🔲"]; $icon_class = IconsHelper::$priority_icons[$value] ?? ['fas fa-question-circle', "🔲"];
$icon_color = IconsHelper::$priority_colors[$value] ?? 'info';
break; break;
default: default:
$icon_class = 'fas fa-question-circle'; $icon_class = 'fas fa-question-circle';
} }
if($type == 'priority'){
// return '<p class="button"><span class="icon is-small">'
return '<span class="icon is-medium">
<i class="'.$icon_class[0].' fa-lg has-text-'.$icon_color.'"></i>
</span>';
} else {
return '<span class="icon is-medium"><i class="'.$icon_class[0].' fa-lg"></i></span>';
}
return '<span class="is-size-5">'.$icon_class[1].'</span>'; return '<span class="is-size-5">'.$icon_class[1].'</span>';
return '<i class="'.$icon_class[0].' is-size-4"></i>';
} }

View File

@ -11,11 +11,10 @@ class Ticket extends \DB\SQL\Mapper {
public function findAll(): array public function findAll(): array
{ {
return $this->db->exec( return $this->db->exec(
'SELECT t.* , tp.name AS priority_name, ts.name AS status_name, u.display_name 'SELECT t.* , tp.name AS priority_name, ts.name AS status_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
LEFT JOIN users u ON t.created_by = u.id
WHERE t.recycled = 0 WHERE t.recycled = 0
ORDER BY t.created_at DESC' ORDER BY t.created_at DESC'
); );

View File

@ -1,10 +0,0 @@
{
"dependencies": {
"bulma": "^1.0.3"
},
"scripts": {
"sass": "sass scss/main.scss public/css/main.css",
"sass:min": "sass scss/main.scss public/css/main.min.css --style compressed",
"sass:watch": "sass --watch scss/main.scss:public/css/main.css"
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -8,8 +8,6 @@ i.fa { font-weight: 100 !important ; }
.table th.th-icon { width: 2rem; } .table th.th-icon { width: 2rem; }
#ticket_list .g-flex-item { border-bottom: 1px solid var(--bulma-text-soft); }
/* List Component */ /* List Component */
.list{ .list{
--be-list-color:var(--bulma-text); --be-list-color:var(--bulma-text);

View File

@ -1,59 +0,0 @@
@use "../vendor/bulma";
.ticket-item {
@extend .is-flex;
@extend .mb-1;
@extend .pt-1;
@extend .pb-2;
@extend .is-align-items-flex-start;
border-bottom: 1px solid var(--bulma-text-90);
.ticket-icon {
@extend .mr-2;
display: flex;
align-items: baseline;
.checkbox {
margin-right: 0.5rem;
}
}
.ticket-content {
@extend .is-flex;
@extend .is-flex-direction-column;
@extend .is-flex-grow-1;
align-self: baseline;
.ticket-header {
@extend .is-flex;
@extend .is-justify-content-flex-start;
@extend .is-flex-wrap-wrap;
align-items: center;
.ticket-title {
@extend .title;
@extend .is-5;
@extend .mb-1;
font-weight: normal;
}
.tags {
@extend .ml-2;
}
}
.ticket-meta {
@extend .is-flex;
align-items: center;
flex-wrap: wrap;
gap: 0.25rem;
p {
@extend .subtitle;
@extend .is-6;
font-weight: 300;
margin: 0;
}
}
}
}

View File

@ -1,5 +0,0 @@
// import bulma
@use "vendor/bulma";
// import custom components
@use "components/ticket-item";

View File

@ -1,3 +0,0 @@
@use "../../node_modules/bulma/sass/utilities/" as bulma-utils;
@use "../../node_modules/bulma/sass/helpers/" as bulma-helpers;
@use "../../node_modules/bulma/sass/elements/" as bulma-elements;

View File

@ -1 +0,0 @@
@forward "../../node_modules/bulma/bulma";

View File

@ -1,34 +0,0 @@
<div class="ticket-item">
<div class="ticket-icon">
<div class="field">
<label class="b-checkbox checkbox">
<input type="checkbox" value="false" />
<span class="check"></span>
<span class="control-label"></span>
</label>
</div>
<span class="icon is-medium"><icons type="status">{{@ticket.status_name}}</icons></span>
</div>
<div class="ticket-content">
<div class="ticket-header">
<span class="ticket-title">
<a href="/ticket/{{ @ticket.id }}">{{ @ticket.title }}</a>
</span>
<div class="tags">
<div class="tags ml-2">
<!-- TODO: get tags -->
<span class="tag is-link">tag</span>
</div>
<?php /*
<repeat group="{{ @ticket.tags }}" value="{{ @tag }}">
<span class="tag is-link">{{ @tag }}</span>
</repeat>
*/ ?>
</div>
</div>
<div class="ticket-meta">
<p>#{{ @ticket.id }} opened {{ @ticket.created_at }} by {{ @ticket.display_name }}</p>
</div>
</div>
</div>

View File

@ -5,16 +5,14 @@
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>TP ServiceDesk</title> <title>TP ServiceDesk</title>
<link rel="stylesheet" href="/style.css">
<link rel="stylesheet" href="/css/main.min.css">
<!-- bulma.io--> <!-- bulma.io-->
<!-- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@1.0.2/css/bulma.min.css"> --> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@1.0.2/css/bulma.min.css">
<!-- bulma helpers --> <!-- bulma helpers -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma-helpers/0.4.3/css/bulma-helpers.min.css" <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma-helpers/0.4.3/css/bulma-helpers.min.css"
integrity="sha512-U6ELnUi7oqVEjkLmFw5r5UR5LEtvpImS/jUykBKneVhD0lxZxfJZ3k3pe003ktrtNZYungd9u3Urp2X09wKwXg==" integrity="sha512-U6ELnUi7oqVEjkLmFw5r5UR5LEtvpImS/jUykBKneVhD0lxZxfJZ3k3pe003ktrtNZYungd9u3Urp2X09wKwXg=="
crossorigin="anonymous" referrerpolicy="no-referrer" /> crossorigin="anonymous" referrerpolicy="no-referrer" />
<!-- bulma-checkbox --> <link rel="stylesheet" href="/style.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma-checkbox@1.2.1/css/main.min.css" integrity="sha256-wvxLpriInkhouxrLZ5oo74cJpCtZJkR9bRJwFDvdd4w=" crossorigin="anonymous"> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma-checkradio@2.1/dist/css/bulma-checkradio.min.css">
<!-- font awesome --> <!-- font awesome -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.7.2/css/all.min.css" <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.7.2/css/all.min.css"
integrity="sha512-Evv84Mr4kqVGRNSgIGL/F/aIDqQb7xQ2vcrdIwxfjThSH8CSR7PBEakCr51Ck+w+/U6swU2Im1vVX0SVk9ABhg==" integrity="sha512-Evv84Mr4kqVGRNSgIGL/F/aIDqQb7xQ2vcrdIwxfjThSH8CSR7PBEakCr51Ck+w+/U6swU2Im1vVX0SVk9ABhg=="

View File

@ -1,8 +0,0 @@
<h1 class="title">Admin</h1>
<include href="/ui/session/error.html">
<hr>
<section>
<p><a href="/admin/priority">Ticket &gt; Priorities</a></p>
<p><a href="/admin/status">Ticket &gt; Statuses</a></p>
</section>

View File

@ -1,2 +0,0 @@
<h1 class="title">Create Ticket Priority</h1>
<p>TODO:</p>

View File

@ -1,33 +0,0 @@
<h1 class="title">Admin: Ticket Priorities</h1>
<include href="/ui/session/error.html">
<p><a class="button" href="/admin/priority/create">create priority</a></p>
<hr>
<table class="table is-fullwidth is-bordered">
<thead>
<tr class="has-background-grey">
<th class="has-text-light">id</th>
<th class="has-text-light">name</th>
<th class="has-text-light">sort_order</th>
<th class="has-text-light"></th>
</tr>
</thead>
<tbody>
<repeat group="{{@priorities}}" value="{{@priority}}">
<tr>
<td>{{@priority.id}}</td>
<td><a href="/admin/priority/{{@priority.id}}">{{@priority.name}}</a></td>
<td>{{@priority.sort_order}}</td>
<td>
<a class="button is-link is-small" href="/admin/priority/{{@priority.id}}/edit">
<i class="fa fa-edit"></i></a>
<a class="button is-danger is-small"
href="/admin/priority/{{@priority.id}}/delete"
onclick="return confirm('Are you sure you want to delete this ticket?');">
<i class="fa fa-trash-can"></i></a>
</td>
</tr>
</repeat>
</tbody>
</table>

View File

@ -1,38 +1,34 @@
<h1 class="title">Tickets</h1> <h1 class="title">Tickets</h1>
<include href="/ui/session/error.html"></include> <include href="/ui/session/error.html">
</include> <p><a class="button" href="/ticket/create">create ticket</a></p>
<!-- updated design -- inspiration gitea -->
<div class="field is-grouped">
<div class="control is-expanded">
<div class="field has-addons is-expanded">
<div class="control is-expanded">
<input class="input" type="text" placeholder="Find a ticket">
</div>
<div class="control">
<button class="button is-info"><span class="icon"><i class="fas fa-magnifying-glass"></i></span></button>
</div>
</div>
</div>
<div class="field">
<div class="control">
<p><a class="button is-primary" href="/ticket/create">create ticket</a></p>
</div>
</div>
</div>
<hr> <hr>
<div id="ticket_list"> <table class="table is-fullwidth is-bordered">
<repeat group="{{@tickets}}" value="{{@ticket}}"> <thead>
<include href="/ui/partials/ticket_item.html"></include> <tr class="has-background-warning">
</repeat> <th>id</th><th>title</th>
</div> <th>status</th><th>priority</th><th>created_at</th>
<th></th>
</tr>
</thead>
<?php <tbody>
/* <repeat group="{{@tickets}}" value="{{@ticket}}">
<div id="ticket_list"> <tr>
<repeat group="{{@tickets}}" value="{{@ticket}}"> <td>{{@ticket.id}}</td>
<include href="/ui/views/ticket/index_row.html"></include> <td><a href="/ticket/{{@ticket.id}}">{{@ticket.title}}</a></td>
</repeat> <td><icons type="status">{{@ticket.status_name}}</icons></td>
</div> <td><icons type="priority">{{@ticket.priority_name}}</icons></td>
*/ <td>{{@ticket.created_at}}</td>
?> <td>
<a class="button is-link is-small" href="/ticket/{{@ticket.id}}/edit">
<i class="fa fa-edit"></i></a>
<a class="button is-danger is-small"
href="/ticket/{{@ticket.id}}/delete"
onclick="return confirm('Are you sure you want to delete this ticket?');">
<i class="fa fa-trash-can"></i></a>
</td>
</tr>
</repeat>
</tbody>
</table>

View File

@ -1,20 +0,0 @@
<div class="g-flex-item is-flex is-align-items-flex-start mb-1 pt-1 pb-2">
<div class="g-flex-item-icon is-align-self-baseline mr-2">
<label class="checkbox mr-2"><input type="checkbox"></label>
<span class="icon is-medium"><icons type="status">{{@ticket.status_name}}</icons></span>
</div>
<div class="g-flex-item-main is-flex is-flex-direction-column is-flex-grow-1 is-align-self-baseline">
<div class="g-flex-item-header is-flex is-justify-content-flex-start is-flex-wrap-wrap">
<div class="">
<span class="title is-5 has-text-weight-normal"><a href="/ticket/{{@ticket.id}}">{{@ticket.title}}</a></span>
</div>
<div class="tags ml-2">
<!-- TODO: get tags -->
<span class="tag is-link">tag</span>
</div>
</div>
<div class="g-flex-item-body is-flex is-align-items-centre flex-wrap gap ">
<p class="subtitle is-6 has-text-weight-light">#{{@ticket.id}} opened 2025-03-25 by {{@ticket.display_name}}</p>
</div>
</div>
</div>

View File

@ -1,12 +1,6 @@
<!-- Ticket - View --> <!-- Ticket - View -->
<div class="is-flex"> <h1 class="title">{{ @ticket.title }}</h1>
<h1 class="title is-flex-grow-1">{{ @ticket.title }}</h1> <p><a href="/ticket/{{ @ticket.id}}/edit">edit ticket</a></p>
<div class="field is-grouped">
<p class="control"><a class="button" href="/ticket/{{ @ticket.id}}/edit">edit ticket</a></p>
<p class="control"><a class="button is-primary" href="/ticket/create">new ticket</a></p>
</div>
</div>
<hr> <hr>
<div class="content"> <div class="content">