Added view ticket

This commit is contained in:
Pinga 2023-12-11 10:42:53 +02:00
parent 2795e814c2
commit 27f912ed1c
5 changed files with 196 additions and 115 deletions

View file

@ -41,6 +41,7 @@ class SupportController extends Controller
}
try {
$db->beginTransaction();
$currentDateTime = new \DateTime();
$crdate = $currentDateTime->format('Y-m-d H:i:s.v');
$db->insert(
@ -62,17 +63,8 @@ class SupportController extends Controller
]
);
$ticket_id = $db->getLastInsertId();
$db->insert(
'ticket_responses',
[
'ticket_id' => $ticket_id,
'responder_id' => $_SESSION['auth_user_id'],
'response' => $message,
'date_created' => $crdate,
]
);
$db->commit();
} catch (Exception $e) {
$db->rollBack();
return view($response, 'admin/support/newticket.twig', [
@ -96,6 +88,86 @@ class SupportController extends Controller
'categories' => $categories,
]);
}
public function viewTicket(Request $request, Response $response, $args)
{
$rawNumber = $args;
$ticketNumber = filter_var($rawNumber, FILTER_VALIDATE_INT);
if ($ticketNumber === false) {
$this->container->get('flash')->addMessage('error', 'Invalid ticket number');
return $response->withHeader('Location', '/support')->withStatus(302);
}
$db = $this->container->get('db');
// Get the current URI
$uri = $request->getUri()->getPath();
$ticket = $db->selectRow('SELECT st.*, u.username AS ticket_creator
FROM support_tickets AS st
JOIN users AS u ON st.user_id = u.id
WHERE st.id = ?', [$ticketNumber]);
if ($ticket) {
$replies = $db->select('SELECT tr.*, u.username AS responder_name
FROM ticket_responses AS tr
JOIN users AS u ON tr.responder_id = u.id
WHERE tr.ticket_id = ?', [$ticketNumber]);
$category = $db->selectValue('SELECT name FROM ticket_categories WHERE id = ?', [$ticket['category_id']]);
// Default view for GET requests or if POST data is not set
return view($response,'admin/support/viewTicket.twig', [
'ticket' => $ticket,
'replies' => $replies,
'category' => $category,
'currentUri' => $uri
]);
} else {
$this->container->get('flash')->addMessage('error', 'Invalid ticket number');
return $response->withHeader('Location', '/support')->withStatus(302);
}
}
public function replyTicket(Request $request, Response $response)
{
if ($request->getMethod() === 'POST') {
// Retrieve POST data
$data = $request->getParsedBody();
$db = $this->container->get('db');
// Get the current URI
$uri = $request->getUri()->getPath();
$categories = $db->select("SELECT * FROM ticket_categories");
$ticket_id = $data['ticket_id'] ?? null;
$responseText = $data['responseText'] ?? null;
if (!$responseText) {
$this->container->get('flash')->addMessage('error', 'Please enter a reply');
return $response->withHeader('Location', '/ticket/'.$ticket_id)->withStatus(302);
}
try {
$currentDateTime = new \DateTime();
$crdate = $currentDateTime->format('Y-m-d H:i:s.v');
$db->insert(
'ticket_responses',
[
'ticket_id' => $ticket_id,
'responder_id' => $_SESSION['auth_user_id'],
'response' => $responseText,
'date_created' => $crdate,
]
);
$this->container->get('flash')->addMessage('success', 'Reply has been created successfully on ' . $crdate);
return $response->withHeader('Location', '/ticket/'.$ticket_id)->withStatus(302);
} catch (Exception $e) {
$this->container->get('flash')->addMessage('error', 'Database error: '.$e->getMessage());
return $response->withHeader('Location', '/ticket/'.$ticket_id)->withStatus(302);
}
}
}
public function docs(Request $request, Response $response)
{

View file

@ -44,6 +44,7 @@
<div class="page-body">
<div class="container-xl">
<div class="col-12">
{% include 'partials/flash.twig' %}
{% if subject is defined %}
<div class="alert alert-important alert-success alert-dismissible" role="alert">
<div class="d-flex">

View file

@ -0,0 +1,110 @@
{% extends "layouts/app.twig" %}
{% block title %}{{ __('Ticket Overview') }}{% endblock %}
{% block content %}
<div class="page-wrapper">
<!-- Page header -->
<div class="page-header d-print-none">
<div class="container-xl">
<div class="row g-2 align-items-center">
<div class="col">
<!-- Page pre-title -->
<div class="page-pretitle">
{{ __('Overview') }}
</div>
<h2 class="page-title">
{{ __('Ticket Overview') }}
</h2>
</div>
</div>
</div>
</div>
<!-- Page body -->
<div class="page-body">
<div class="container-xl">
<div class="col-12">
{% include 'partials/flash.twig' %}
<form action="/support/reply" method="post">
{{ csrf.field | raw }}
<input type="hidden" name="ticket_id" value="{{ ticket.id }}">
<div class="card">
<div class="card-header">
<h5 class="card-title">Ticket #{{ ticket.id }} - {{ ticket.subject }}</h5>&nbsp;
{% if ticket.status == 'Open' %}
<span class="badge bg-success-lt">{{ ticket.status }}</span>
{% elseif ticket.status == 'In Progress' %}
<span class="badge bg-warning-lt">{{ ticket.status }}</span>
{% elseif ticket.status == 'Resolved' %}
<span class="badge bg-info-lt">{{ ticket.status }}</span>
{% elseif ticket.status == 'Closed' %}
<span class="badge bg-secondary-lt">{{ ticket.status }}</span>
{% else %}
<span class="badge bg-dark-lt">Unknown Status</span>
{% endif %}
</div>
<div class="card-body">
<h6 class="card-subtitle mb-2 text-muted">Ticket Details</h6>
<p><span>Created on:</span> <strong>{{ ticket.date_created }}</strong></p>
<p><span>Category:</span> <strong>{{ category }}</strong></p>
<p><span>Priority:</span> <strong>{{ ticket.priority }}</strong></p>
<div class="card mt-3">
<div class="card-body">
<h6 class="card-subtitle mb-2 text-muted">Conversation</h6>
{% for reply in replies %}
<div class="d-flex align-items-start mb-3">
<div class="flex-shrink-0">
<span class="avatar">{{ reply.responder_name|slice(0, 2) }}</span>
</div>
<div class="flex-grow-1 ms-3">
<strong>{{ reply.responder_name }}</strong>
<small class="text-muted">{{ reply.date_created|date("Y-m-d H:i") }}</small>
<p>{{ reply.response }}</p>
</div>
</div>
{% endfor %}
<div class="d-flex align-items-start mb-3">
<div class="flex-shrink-0">
<span class="avatar">{{ ticket.ticket_creator|slice(0, 2) }}</span>
</div>
<div class="flex-grow-1 ms-3">
<strong>{{ ticket.ticket_creator }}</strong> <small class="text-muted">{{ ticket.date_created|date("Y-m-d H:i") }}</small>
<p>{{ ticket.message }}</p>
</div>
</div>
</div>
</div>
<div class="mt-3">
<label for="responseText" class="form-label">Your Response</label>
<textarea class="form-control" id="responseText" name="responseText" rows="3" required></textarea>
</div>
</div>
<div class="card-footer">
<div class="row align-items-center">
<div class="col-auto">
<button type="submit" class="btn btn-primary">Submit Response</button>
</div>
</div>
</div>
</div>
</form>
</div>
</div>
</div>
<footer class="footer footer-transparent d-print-none">
<div class="container-xl">
<div class="col-12 col-lg-auto mt-3 mt-lg-0">
<ul class="list-inline list-inline-dots mb-0">
<li class="list-inline-item">
Copyright &copy; 2023
<a href="https://namingo.org" target="_blank" class="link-secondary">Namingo</a>.
</li>
</ul>
</div>
</div>
</div>
</footer>
</div>
{% endblock %}

View file

@ -219,7 +219,7 @@
</a>
</div>
</li>
<li {{ is_current_url('ticketview') or is_current_url('newticket') or is_current_url('docs') or is_current_url('mediakit') ? 'class="nav-item dropdown active"' : 'class="nav-item dropdown"' }}>
<li {{ is_current_url('ticketview') or is_current_url('newticket') or is_current_url('docs') or is_current_url('mediakit') or 'ticket' in currentUri ? 'class="nav-item dropdown active"' : 'class="nav-item dropdown"' }}>
<a class="nav-link dropdown-toggle" href="#" data-bs-toggle="dropdown" data-bs-auto-close="outside" role="button" aria-expanded="false">
<span class="nav-link-icon d-md-none d-lg-inline-block"><svg xmlns="http://www.w3.org/2000/svg" class="icon" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M12 12m-4 0a4 4 0 1 0 8 0a4 4 0 1 0 -8 0"></path><path d="M12 12m-9 0a9 9 0 1 0 18 0a9 9 0 1 0 -18 0"></path><path d="M15 15l3.35 3.35"></path><path d="M9 15l-3.35 3.35"></path><path d="M5.65 5.65l3.35 3.35"></path><path d="M18.35 5.65l-3.35 3.35"></path></svg>
</span>
@ -250,110 +250,6 @@
</div>
{% block content %}{% endblock %}
</div>
{% if route_is('dashboard') %}
<div class="modal modal-blur fade" id="modal-report" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog modal-lg" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">New report</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="mb-3">
<label class="form-label">Name</label>
<input type="text" class="form-control" name="example-text-input" placeholder="Your report name">
</div>
<label class="form-label">Report type</label>
<div class="form-selectgroup-boxes row mb-3">
<div class="col-lg-6">
<label class="form-selectgroup-item">
<input type="radio" name="report-type" value="1" class="form-selectgroup-input" checked>
<span class="form-selectgroup-label d-flex align-items-center p-3">
<span class="me-3">
<span class="form-selectgroup-check"></span>
</span>
<span class="form-selectgroup-label-content">
<span class="form-selectgroup-title strong mb-1">Simple</span>
<span class="d-block text-muted">Provide only basic data needed for the report</span>
</span>
</span>
</label>
</div>
<div class="col-lg-6">
<label class="form-selectgroup-item">
<input type="radio" name="report-type" value="1" class="form-selectgroup-input">
<span class="form-selectgroup-label d-flex align-items-center p-3">
<span class="me-3">
<span class="form-selectgroup-check"></span>
</span>
<span class="form-selectgroup-label-content">
<span class="form-selectgroup-title strong mb-1">Advanced</span>
<span class="d-block text-muted">Insert charts and additional advanced analyses to be inserted in the report</span>
</span>
</span>
</label>
</div>
</div>
<div class="row">
<div class="col-lg-8">
<div class="mb-3">
<label class="form-label">Report url</label>
<div class="input-group input-group-flat">
<span class="input-group-text">
https://tabler.io/reports/
</span>
<input type="text" class="form-control ps-0" value="report-01" autocomplete="off">
</div>
</div>
</div>
<div class="col-lg-4">
<div class="mb-3">
<label class="form-label">Visibility</label>
<select class="form-select">
<option value="1" selected>Private</option>
<option value="2">Public</option>
<option value="3">Hidden</option>
</select>
</div>
</div>
</div>
</div>
<div class="modal-body">
<div class="row">
<div class="col-lg-6">
<div class="mb-3">
<label class="form-label">Client name</label>
<input type="text" class="form-control">
</div>
</div>
<div class="col-lg-6">
<div class="mb-3">
<label class="form-label">Reporting period</label>
<input type="date" class="form-control">
</div>
</div>
<div class="col-lg-12">
<div>
<label class="form-label">Additional information</label>
<textarea class="form-control" rows="3"></textarea>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<a href="#" class="btn btn-link link-secondary" data-bs-dismiss="modal">
Cancel
</a>
<a href="#" class="btn btn-primary ms-auto" data-bs-dismiss="modal">
<!-- Download SVG icon from http://tabler-icons.io/i/plus -->
<svg xmlns="http://www.w3.org/2000/svg" class="icon" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><line x1="12" y1="5" x2="12" y2="19" /><line x1="5" y1="12" x2="19" y2="12" /></svg>
Create new report
</a>
</div>
</div>
</div>
</div>
{% endif %}
<!-- Libs JS -->
{% if route_is('dashboard') %}
{% include 'partials/js-dash.twig' %}

View file

@ -96,6 +96,8 @@ $app->group('', function ($route) {
$route->get('/support', SupportController::class .':view')->setName('ticketview');
$route->map(['GET', 'POST'], '/support/new', SupportController::class .':newticket')->setName('newticket');
$route->get('/ticket/{ticket}', SupportController::class . ':viewTicket')->setName('viewTicket');
$route->post('/support/reply', SupportController::class . ':replyTicket')->setName('replyTicket');
$route->get('/support/docs', SupportController::class .':docs')->setName('docs');
$route->get('/support/media', SupportController::class .':mediakit')->setName('mediakit');