mirror of
https://github.com/getnamingo/registry.git
synced 2025-05-29 17:00:06 +02:00
Added view ticket
This commit is contained in:
parent
2795e814c2
commit
27f912ed1c
5 changed files with 196 additions and 115 deletions
|
@ -41,6 +41,7 @@ class SupportController extends Controller
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
$db->beginTransaction();
|
||||||
$currentDateTime = new \DateTime();
|
$currentDateTime = new \DateTime();
|
||||||
$crdate = $currentDateTime->format('Y-m-d H:i:s.v');
|
$crdate = $currentDateTime->format('Y-m-d H:i:s.v');
|
||||||
$db->insert(
|
$db->insert(
|
||||||
|
@ -62,17 +63,8 @@ class SupportController extends Controller
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
$ticket_id = $db->getLastInsertId();
|
$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) {
|
} catch (Exception $e) {
|
||||||
$db->rollBack();
|
$db->rollBack();
|
||||||
return view($response, 'admin/support/newticket.twig', [
|
return view($response, 'admin/support/newticket.twig', [
|
||||||
|
@ -96,6 +88,86 @@ class SupportController extends Controller
|
||||||
'categories' => $categories,
|
'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)
|
public function docs(Request $request, Response $response)
|
||||||
{
|
{
|
||||||
|
|
|
@ -44,6 +44,7 @@
|
||||||
<div class="page-body">
|
<div class="page-body">
|
||||||
<div class="container-xl">
|
<div class="container-xl">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
|
{% include 'partials/flash.twig' %}
|
||||||
{% if subject is defined %}
|
{% if subject is defined %}
|
||||||
<div class="alert alert-important alert-success alert-dismissible" role="alert">
|
<div class="alert alert-important alert-success alert-dismissible" role="alert">
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
|
|
110
cp/resources/views/admin/support/viewTicket.twig
Normal file
110
cp/resources/views/admin/support/viewTicket.twig
Normal 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>
|
||||||
|
{% 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 © 2023
|
||||||
|
<a href="https://namingo.org" target="_blank" class="link-secondary">Namingo</a>.
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
|
@ -219,7 +219,7 @@
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</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">
|
<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 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>
|
</span>
|
||||||
|
@ -250,110 +250,6 @@
|
||||||
</div>
|
</div>
|
||||||
{% block content %}{% endblock %}
|
{% block content %}{% endblock %}
|
||||||
</div>
|
</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 -->
|
<!-- Libs JS -->
|
||||||
{% if route_is('dashboard') %}
|
{% if route_is('dashboard') %}
|
||||||
{% include 'partials/js-dash.twig' %}
|
{% include 'partials/js-dash.twig' %}
|
||||||
|
|
|
@ -96,6 +96,8 @@ $app->group('', function ($route) {
|
||||||
|
|
||||||
$route->get('/support', SupportController::class .':view')->setName('ticketview');
|
$route->get('/support', SupportController::class .':view')->setName('ticketview');
|
||||||
$route->map(['GET', 'POST'], '/support/new', SupportController::class .':newticket')->setName('newticket');
|
$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/docs', SupportController::class .':docs')->setName('docs');
|
||||||
$route->get('/support/media', SupportController::class .':mediakit')->setName('mediakit');
|
$route->get('/support/media', SupportController::class .':mediakit')->setName('mediakit');
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue