Ticket System

This commit is contained in:
SahrulGnwn 2022-08-01 23:52:16 +07:00
parent 812126e120
commit 94bbea2690
27 changed files with 1391 additions and 2 deletions

View file

@ -0,0 +1,62 @@
<?php
namespace App\Http\Controllers\Mod;
use App\Models\User;
use App\Models\Ticket;
use App\Models\Server;
use App\Models\TicketCategory;
use App\Models\TicketComment;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Cache;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use App\Notifications\Ticket\User\ReplyNotification;
class TicketsController extends Controller
{
public function index() {
$tickets = Ticket::paginate(10);
$ticketcategories = TicketCategory::all();
return view("admin.ticket.index", compact("tickets", "ticketcategories"));
}
public function show($ticket_id) {
$ticket = Ticket::where("ticket_id", $ticket_id)->firstOrFail();
$ticketcomments = $ticket->ticketcomments;
$ticketcategory = $ticket->ticketcategory;
$server = Server::where('id', $ticket->server)->first();
return view("admin.ticket.show", compact("ticket", "ticketcategory", "ticketcomments", "server"));
}
public function close($ticket_id) {
$ticket = Ticket::where("ticket_id", $ticket_id)->firstOrFail();
$ticket->status = "Closed";
$ticket->save();
$ticketOwner = $ticket->user;
return redirect()->back()->with('success', __('A ticket has been closed, ID: #') . $ticket->ticket_id);
}
public function delete($ticket_id){
$ticket = Ticket::where("ticket_id", $ticket_id)->firstOrFail();
TicketComment::where("ticket_id", $ticket->id)->delete();
$ticket->delete();
return redirect()->back()->with('success', __('A ticket has been deleted, ID: #') . $ticket_id);
}
public function reply(Request $request) {
$this->validate($request, array("ticketcomment" => "required"));
$ticket = Ticket::where('id', $request->input("ticket_id"))->firstOrFail();
$ticket->status = "Answered";
$ticket->update();
$ticketcomment = TicketComment::create(array(
"ticket_id" => $request->input("ticket_id"),
"user_id" => Auth::user()->id,
"ticketcomment" => $request->input("ticketcomment"),
));
$user = User::where('id', $ticket->user_id)->firstOrFail();
$newmessage = $request->input("ticketcomment");
$user->notify(new ReplyNotification($ticket, $user, $newmessage));
return redirect()->back()->with('success', __('Your comment has been submitted'));
}
}

View file

@ -0,0 +1,84 @@
<?php
namespace App\Http\Controllers;
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Session;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Str;
use Illuminate\Support\Facades\Notification;
use App\Models\Ticket;
use App\Models\Server;
use App\Models\TicketComment;
use App\Models\TicketCategory;
use App\Notifications\Ticket\User\CreateNotification;
use App\Notifications\Ticket\Admin\AdminCreateNotification;
use App\Notifications\Ticket\Admin\AdminReplyNotification;
class TicketsController extends Controller
{
public function index()
{
$tickets = Ticket::where("user_id", Auth::user()->id)->paginate(10);
$ticketcategories = TicketCategory::all();
return view("ticket.index", compact("tickets", "ticketcategories"));
}
public function create() {
$ticketcategories = TicketCategory::all();
$servers = Auth::user()->servers;
return view("ticket.create", compact("ticketcategories", "servers"));
}
public function store(Request $request) {
$this->validate($request, array(
"title" => "required",
"ticketcategory" => "required",
"priority" => "required",
"message" => "required")
);
$ticket = new Ticket(array(
"title" => $request->input("title"),
"user_id" => Auth::user()->id,
"ticket_id" => strtoupper(Str::random(5)),
"ticketcategory_id" => $request->input("ticketcategory"),
"priority" => $request->input("priority"),
"message" => $request->input("message"),
"status" => "Open",
"server" => $request->input("server"))
);
$ticket->save();
$user = Auth::user();
$admin = User::where('role', 'admin')->orWhere('role', 'mod')->get();
$user->notify(new CreateNotification($ticket));
Notification::send($admin, new AdminCreateNotification($ticket, $user));
return redirect()->route('ticket.index')->with('success', __('A ticket has been opened, ID: #') . $ticket->ticket_id);
}
public function show($ticket_id) {
$ticket = Ticket::where("ticket_id", $ticket_id)->firstOrFail();
$ticketcomments = $ticket->ticketcomments;
$ticketcategory = $ticket->ticketcategory;
$server = Server::where('id', $ticket->server)->first();
return view("ticket.show", compact("ticket", "ticketcategory", "ticketcomments", "server"));
}
public function reply(Request $request) {
$this->validate($request, array("ticketcomment" => "required"));
$ticket = Ticket::where('id', $request->input("ticket_id"))->firstOrFail();
$ticket->status = "Client Reply";
$ticket->update();
$ticketcomment = TicketComment::create(array(
"ticket_id" => $request->input("ticket_id"),
"user_id" => Auth::user()->id,
"ticketcomment" => $request->input("ticketcomment"),
"message" => $request->input("message")
));
$user = Auth::user();
$admin = User::where('role', 'admin')->orWhere('role', 'mod')->get();
$newmessage = $request->input("ticketcomment");
Notification::send($admin, new AdminReplyNotification($ticket, $user, $newmessage));
return redirect()->back()->with('success', __('Your comment has been submitted'));
}
}

View file

@ -6,6 +6,7 @@ use App\Http\Middleware\ApiAuthToken;
use App\Http\Middleware\CheckSuspended;
use App\Http\Middleware\GlobalNames;
use App\Http\Middleware\isAdmin;
use App\Http\Middleware\isMod;
use App\Http\Middleware\LastSeen;
use Illuminate\Foundation\Http\Kernel as HttpKernel;
@ -72,6 +73,7 @@ class Kernel extends HttpKernel
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
'admin' => isAdmin::class,
'mod' => isMod::class,
'api.token' => ApiAuthToken::class,
'checkSuspended' => CheckSuspended::class
];

View file

@ -0,0 +1,27 @@
<?php
namespace App\Http\Middleware;
use App\Providers\RouteServiceProvider;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
class isMod
{
/**
* Handle an incoming request.
*
* @param Request $request
* @param Closure $next
* @return mixed
*/
public function handle(Request $request, Closure $next)
{
if (Auth::user() && Auth::user()->role == 'mod' || Auth::user() && Auth::user()->role == 'admin') {
return $next($request);
}
return redirect(RouteServiceProvider::HOME);
}
}

21
app/Models/Ticket.php Normal file
View file

@ -0,0 +1,21 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Ticket extends Model {
protected $fillable = [
'user_id', 'ticketcategory_id', 'ticket_id', 'title', 'priority', 'message', 'status', 'server'
];
public function ticketcategory(){
return $this->belongsTo(TicketCategory::class);}
public function ticketcomments(){
return $this->hasMany(TicketComment::class);}
public function user(){
return $this->belongsTo(User::class);}
}

View file

@ -0,0 +1,13 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class TicketCategory extends Model {
protected $fillable = ['name'];
public function tickets(){
return $this->hasMany(Ticket::class);}
}

View file

@ -0,0 +1,21 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class TicketComment extends Model {
protected $fillable = [
'ticket_id', 'user_id', 'ticketcomment'
];
public function ticketcategory(){
return $this->belongsTo(TicketCategory::class);}
public function ticket(){
return $this->belongsTo(Ticket::class);}
public function user(){
return $this->belongsTo(User::class);}
}

View file

@ -0,0 +1,71 @@
<?php
namespace App\Notifications\Ticket\Admin;
use App\Models\Ticket;
use App\Models\User;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification;
class AdminCreateNotification extends Notification implements ShouldQueue
{
//THIS IS BASICALLY NOT USED ANYMORE WITH INVOICENOTIFICATION IN PLACE
use Queueable;
private Ticket $ticket;
private User $user;
/**
* Create a new notification instance.
*
* @return void
*/
public function __construct(Ticket $ticket, User $user)
{
$this->ticket = $ticket;
$this->user = $user;
}
/**
* Get the notification's delivery channels.
*
* @param mixed $notifiable
* @return array
*/
public function via($notifiable)
{
$via = ['mail','database'];
return $via;
}
/**
* Get the mail representation of the notification.
*
* @param mixed $notifiable
* @return MailMessage
*/
public function toMail($notifiable)
{
return (new MailMessage)
->subject('[Ticket ID: ' . $this->ticket->ticket_id . '] ' . $this->ticket->title)
->markdown('mail.ticket.admin.create' , ['ticket' => $this->ticket, 'user' => $this->user]);
}
/**
* Get the array representation of the notification.
*
* @param mixed $notifiable
* @return array
*/
public function toArray($notifiable)
{
return [
'title' => '[Ticket ID: ' . $this->ticket->ticket_id . '] ' . $this->ticket->title,
'content' => "Ticket With ID : {$this->ticket->ticket_id} has been opened by <strong>{$this->user->name}</strong>",
];
}
}

View file

@ -0,0 +1,78 @@
<?php
namespace App\Notifications\Ticket\Admin;
use App\Models\Ticket;
use App\Models\User;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification;
class AdminReplyNotification extends Notification implements ShouldQueue
{
//THIS IS BASICALLY NOT USED ANYMORE WITH INVOICENOTIFICATION IN PLACE
use Queueable;
private Ticket $ticket;
private User $user;
private $newmessage;
/**
* Create a new notification instance.
*
* @return void
*/
public function __construct(Ticket $ticket, User $user, $newmessage)
{
$this->ticket = $ticket;
$this->user = $user;
$this->newmessage = $newmessage;
}
/**
* Get the notification's delivery channels.
*
* @param mixed $notifiable
* @return array
*/
public function via($notifiable)
{
$via = ['mail','database'];
return $via;
}
/**
* Get the mail representation of the notification.
*
* @param mixed $notifiable
* @return MailMessage
*/
public function toMail($notifiable)
{
return (new MailMessage)
->subject('[Ticket ID: ' . $this->ticket->ticket_id . '] ' . $this->ticket->title)
->markdown('mail.ticket.admin.reply' , ['ticket' => $this->ticket, 'user' => $this->user, 'newmessage' => $this->newmessage]);
}
/**
* Get the array representation of the notification.
*
* @param mixed $notifiable
* @return array
*/
public function toArray($notifiable)
{
return [
'title' => '[Ticket ID: ' . $this->ticket->ticket_id . '] ' . $this->ticket->title,
'content' => "
<p>Ticket With ID : {$this->ticket->ticket_id} has had a new reply posted by <strong>{$this->user->name}</strong></p>
<br>
<p><strong>Message:</strong></p>
<p>{$this->newmessage}</p>
",
];
}
}

View file

@ -0,0 +1,68 @@
<?php
namespace App\Notifications\Ticket\User;
use App\Models\Ticket;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification;
class CreateNotification extends Notification implements ShouldQueue
{
//THIS IS BASICALLY NOT USED ANYMORE WITH INVOICENOTIFICATION IN PLACE
use Queueable;
private Ticket $ticket;
/**
* Create a new notification instance.
*
* @return void
*/
public function __construct(Ticket $ticket)
{
$this->ticket = $ticket;
}
/**
* Get the notification's delivery channels.
*
* @param mixed $notifiable
* @return array
*/
public function via($notifiable)
{
$via = ['mail','database'];
return $via;
}
/**
* Get the mail representation of the notification.
*
* @param mixed $notifiable
* @return MailMessage
*/
public function toMail($notifiable)
{
return (new MailMessage)
->subject('[Ticket ID: ' . $this->ticket->ticket_id . '] ' . $this->ticket->title)
->markdown('mail.ticket.user.create' , ['ticket' => $this->ticket]);
}
/**
* Get the array representation of the notification.
*
* @param mixed $notifiable
* @return array
*/
public function toArray($notifiable)
{
return [
'title' => '[Ticket ID: ' . $this->ticket->ticket_id . '] ' . $this->ticket->title,
'content' => "Your Ticket has been Created With ID : {$this->ticket->ticket_id}",
];
}
}

View file

@ -0,0 +1,78 @@
<?php
namespace App\Notifications\Ticket\User;
use App\Models\User;
use App\Models\Ticket;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification;
class ReplyNotification extends Notification implements ShouldQueue
{
//THIS IS BASICALLY NOT USED ANYMORE WITH INVOICENOTIFICATION IN PLACE
use Queueable;
private Ticket $ticket;
private User $user;
private $newmessage;
/**
* Create a new notification instance.
*
* @return void
*/
public function __construct(Ticket $ticket, User $user, $newmessage)
{
$this->ticket = $ticket;
$this->user = $user;
$this->newmessage = $newmessage;
}
/**
* Get the notification's delivery channels.
*
* @param mixed $notifiable
* @return array
*/
public function via($notifiable)
{
$via = ['mail','database'];
return $via;
}
/**
* Get the mail representation of the notification.
*
* @param mixed $notifiable
* @return MailMessage
*/
public function toMail($notifiable)
{
return (new MailMessage)
->subject('[Ticket ID: ' . $this->ticket->ticket_id . '] ' . $this->ticket->title)
->markdown('mail.ticket.user.reply' , ['ticket' => $this->ticket, 'user' => $this->user, 'newmessage' => $this->newmessage]);
}
/**
* Get the array representation of the notification.
*
* @param mixed $notifiable
* @return array
*/
public function toArray($notifiable)
{
return [
'title' => '[Ticket ID: ' . $this->ticket->ticket_id . '] ' . $this->ticket->title,
'content' => "
<p>Ticket With ID : {$this->ticket->ticket_id} A response has been added to your ticket. Please see below for our response!</p>
<br>
<p><strong>Message:</strong></p>
<p>{$this->newmessage}</p>
",
];
}
}

View file

@ -0,0 +1,39 @@
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateTicketsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('tickets', function (Blueprint $table) {
$table->increments('id');
$table->integer('user_id')->unsigned();
$table->integer('ticketcategory_id')->unsigned();
$table->string('ticket_id')->unique();
$table->string('title');
$table->string('priority');
$table->text('message');
$table->string('status');
$table->string('server')->nullable();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('tickets');
}
}

View file

@ -0,0 +1,59 @@
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateTicketCategoriesTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('ticket_categories', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->timestamps();
});
DB::table('ticket_categories')->insert(
array(
'name' => 'Technical',
)
);
DB::table('ticket_categories')->insert(
array(
'name' => 'Billing',
)
);
DB::table('ticket_categories')->insert(
array(
'name' => 'Issue',
)
);
DB::table('ticket_categories')->insert(
array(
'name' => 'Request',
)
);
DB::table('ticket_categories')->insert(
array(
'name' => 'Other',
)
);
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('ticket_categories');
}
}

View file

@ -0,0 +1,34 @@
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateTicketCommentsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('ticket_comments', function (Blueprint $table) {
$table->increments('id');
$table->integer('ticket_id')->unsigned();
$table->integer('user_id')->unsigned();
$table->text('ticketcomment');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('ticket_comments');
}
}

View file

@ -104,6 +104,9 @@
value="admin">
{{__(' Administrator')}}
</option>
<option @if($user->role == 'mod') selected @endif class="text-info" value="mod">
{{__(' Moderator')}}
</option>
<option @if($user->role == 'client') selected @endif class="text-success"
value="client">
{{__('Client')}}

View file

@ -72,7 +72,7 @@
</div>
<div class="col-lg-8">
<span style="max-width: 250px;"
class="d-inline-block text-truncate badge {{$user->role == 'admin' ? 'badge-info' : 'badge-secondary'}}">
class="d-inline-block text-truncate badge {{$user->role == 'admin' || $user->role == 'mod' ? 'badge-info' : 'badge-secondary'}}">
{{$user->role}}
</span>
</div>

View file

@ -225,7 +225,24 @@
</a>
</li>
@endif
<li class="nav-item">
<a href="{{ route('ticket.index') }}" class="nav-link @if (Request::routeIs('ticket.*')) active @endif">
<i class="nav-icon fas fas fa-ticket-alt"></i>
<p>{{ __('Support Ticket') }}</p>
</a>
</li>
@if (Auth::user()->role == 'admin' || Auth::user()->role == 'mod')
<li class="nav-header">{{ __('Moderation') }}</li>
<li class="nav-item">
<a href="{{ route('mod.ticket.index') }}" class="nav-link @if (Request::routeIs('mod.ticket.*')) active @endif">
<i class="nav-icon fas fa-ticket-alt"></i>
<p>{{ __('Ticket List') }}</p>
</a>
</li>
@endif
@if (Auth::user()->role == 'admin')
<li class="nav-header">{{ __('Administration') }}</li>

View file

@ -0,0 +1,25 @@
@component('mail::message')
Ticket #{{$ticket->ticket_id}} has been opened by **{{$user->name}}**
### Details:
Client: {{$user->name}} <br>
Subject: {{$ticket->title}} <br>
Category: {{ $ticket->ticketcategory->name }} <br>
Priority: {{ $ticket->priority }} <br>
Status: {{ $ticket->status }} <br>
___
```
{{ $ticket->message }}
```
___
<br>
You can respond to this ticket by simply replying to this email or through the admin area at the url below.
<br>
{{ route('mod.ticket.show', ['ticket_id' => $ticket->ticket_id]) }}
<br>
{{__('Thanks')}},<br>
{{ config('app.name') }}
@endcomponent

View file

@ -0,0 +1,25 @@
@component('mail::message')
Ticket #{{$ticket->ticket_id}} has had a new reply posted by **{{$user->name}}**
### Details
Client: {{$user->name}} <br>
Subject: {{$ticket->title}} <br>
Category: {{ $ticket->ticketcategory->name }} <br>
Priority: {{ $ticket->priority }} <br>
Status: {{ $ticket->status }} <br>
___
```
{{ $newmessage }}
```
___
<br>
You can respond to this ticket by simply replying to this email or through the admin area at the url below.
<br>
{{ route('mod.ticket.show', ['ticket_id' => $ticket->ticket_id]) }}
<br>
{{__('Thanks')}},<br>
{{ config('app.name') }}
@endcomponent

View file

@ -0,0 +1,13 @@
@component('mail::message')
Hello {{$ticket->user->name}},
This is a notification that we have received your support request, and your ticket number is **#{{$ticket->ticket_id}}**.
We will be responding to this ticket as soon as possible. If this is a Setup request, please understand that these requests take longer than regular support timeframes. Please be aware that Setups may take up to 48 hours to be completed.
Thank you so much for being so understanding.
<br>
{{__('Thanks')}},<br>
{{ config('app.name') }}
@endcomponent

View file

@ -0,0 +1,18 @@
@component('mail::message')
A response has been added to your ticket. Please see below for our response!
### Details
Ticket ID : {{ $ticket->ticket_id }} <br>
Subject: {{ $ticket->title }} <br>
Status: {{ $ticket->status }} <br>
___
```
{{ $newmessage }}
```
___
<br>
<br>
{{__('Thanks')}},<br>
{{ config('app.name') }}
@endcomponent

View file

@ -0,0 +1,104 @@
@extends('layouts.main')
@section('content')
<!-- CONTENT HEADER -->
<section class="content-header">
<div class="container-fluid">
<div class="row mb-2">
<div class="col-sm-6">
<h1>{{__('Ticket')}}</h1>
</div>
<div class="col-sm-6">
<ol class="breadcrumb float-sm-right">
<li class="breadcrumb-item"><a href="{{route('home')}}">{{__('Dashboard')}}</a></li>
<li class="breadcrumb-item"><a class="text-muted"
href="{{route('mod.ticket.index')}}">{{__('Ticket List')}}</a></li>
</ol>
</div>
</div>
</div>
</section>
<!-- END CONTENT HEADER -->
<!-- MAIN CONTENT -->
<section class="content">
<div class="container-fluid">
<div class="card">
<div class="card-header">
<div class="d-flex justify-content-between">
<h5 class="card-title"><i class="fas fa-ticket-alt mr-2"></i>{{__('Ticket List')}}</h5>
</div>
</div>
<div class="card-body table-responsive">
<table id="datatable" class="table table-striped">
<thead>
<tr>
<th>Category</th>
<th>Title</th>
<th>User</th>
<th>Status</th>
<th>Last Updated</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
@foreach ($tickets as $ticket)
<tr>
<td>
{{ $ticket->ticketcategory->name }}
</td>
<td>
<a href="{{ route('mod.ticket.show', ['ticket_id' => $ticket->ticket_id]) }}">
#{{ $ticket->ticket_id }} - {{ $ticket->title }}
</a>
</td>
<td>
<a href="/admin/users/{{$ticket->user->id}}">
{{ $ticket->user->name }}
</a>
</td>
<td>
@if ($ticket->status === 'Open')
<span class="badge badge-success">Open</span>
@elseif ($ticket->status === 'Closed')
<span class="badge badge-danger">Closed</span>
@elseif ($ticket->status === 'Answered')
<span class="badge badge-info">Answered</span>
@elseif ($ticket->status === 'Client Reply')
<span class="badge badge-warning">Client Reply</span>
@endif
</td>
<td>{{ $ticket->updated_at }}</td>
<td>
<a data-content="View" data-toggle="popover" data-trigger="hover" data-placement="top" href="{{ route('mod.ticket.show', ['ticket_id' => $ticket->ticket_id]) }}" class="btn btn-sm text-white btn-info mr-1"><i class="fas fa-eye"></i></a>
<form class="d-inline" action="{{ route('mod.ticket.close', ['ticket_id' => $ticket->ticket_id ]) }}" method="POST">
@csrf
<button data-content="Close" data-toggle="popover" data-trigger="hover" data-placement="top" type="submit" class="btn btn-sm text-white btn-warning mr-1"><i class="fas fa-times"></i></button>
</form>
<form class="d-inline" action="{{ route('mod.ticket.delete', ['ticket_id' => $ticket->ticket_id ]) }}" method="POST">
@csrf
<button data-content="Delete" data-toggle="popover" data-trigger="hover" data-placement="top" type="submit" class="btn btn-sm text-white btn-danger mr-1"><i class="fas fa-trash"></i></button>
</form>
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
</div>
<!-- END CUSTOM CONTENT -->
</section>
<!-- END CONTENT -->
@endsection

View file

@ -0,0 +1,141 @@
@extends('layouts.main')
@section('content')
<!-- CONTENT HEADER -->
<section class="content-header">
<div class="container-fluid">
<div class="row mb-2">
<div class="col-sm-6">
<h1>{{ __('Ticket') }}</h1>
</div>
<div class="col-sm-6">
<ol class="breadcrumb float-sm-right">
<li class="breadcrumb-item"><a href="{{ route('home') }}">{{ __('Dashboard') }}</a></li>
<li class="breadcrumb-item"><a class="text-muted"
href="{{ route('mod.ticket.index') }}">{{ __('Ticket') }}</a>
</li>
</ol>
</div>
</div>
</div>
</section>
<!-- END CONTENT HEADER -->
<!-- MAIN CONTENT -->
<section class="content">
<div class="container-fluid">
<div class="row">
<div class="col-lg-12">
<div class="card">
<div class="card-header">
<div class="d-flex justify-content-between">
<h5 class="card-title"><i class="fas fa-users mr-2"></i>#{{ $ticket->ticket_id }}</h5>
</div>
</div>
<div class="card-body">
<div class="ticket-info">
@if(!empty($server))
<p><b>Server:</b> <a href="{{ config("SETTINGS::SYSTEM:PTERODACTYL:URL") . '/admin/servers/view/' . $server->pterodactyl_id }}" target="__blank">{{ $server->name }}</a></p>
@endif
<p><b>Title:</b> {{ $ticket->title }}</p>
<p><b>Category:</b> {{ $ticketcategory->name }}</p>
<p>
@if ($ticket->status === 'Open')
<b>Status:</b> <span class="badge badge-success">Open</span>
@elseif ($ticket->status === 'Closed')
<b>Status:</b> <span class="badge badge-danger">Closed</span>
@elseif ($ticket->status === 'Answered')
<b>Status:</b> <span class="badge badge-info">Answered</span>
@elseif ($ticket->status === 'Client Reply')
<b>Status:</b> <span class="badge badge-warning">Client Reply</span>
@endif
</p>
<p><b>Created on:</b> {{ $ticket->created_at->diffForHumans() }}</p>
</div>
</div>
</div>
</div>
<div class="col-lg-12">
<div class="card">
<div class="card-header">
<div class="d-flex justify-content-between">
<h5 class="card-title"><i class="fas fa-cloud mr-2"></i>{{__('Comment')}}</h5>
</div>
</div>
<div class="card-body">
<div class="card">
<div class="card-header">
<div class="d-flex justify-content-between">
<h5 class="card-title"><img
src="https://www.gravatar.com/avatar/{{ md5(strtolower($ticket->user->email)) }}?s=25"
class="user-image" alt="User Image">
<a href="/admin/users/{{$ticket->user->id}}">{{ $ticket->user->name }}</a>
@if($ticket->user->role === "member")
<span class="badge badge-secondary"> Member </span>
@elseif ($ticket->user->role === "client")
<span class="badge badge-success"> Client </span>
@elseif ($ticket->user->role === "mod")
<span class="badge badge-info"> Moderator </span>
@elseif ($ticket->user->role === "admin")
<span class="badge badge-danger"> Admin </span>
@endif
</h5>
<span class="badge badge-primary">{{ $ticket->created_at->diffForHumans() }}</span>
</div>
</div>
<div class="card-body">
{{ $ticket->message }}
</div>
</div>
@foreach ($ticketcomments as $ticketcomment)
<div class="card">
<div class="card-header">
<div class="d-flex justify-content-between">
<h5 class="card-title"><img
src="https://www.gravatar.com/avatar/{{ md5(strtolower($ticketcomment->user->email)) }}?s=25"
class="user-image" alt="User Image">
<a href="/admin/users/{{$ticketcomment->user->id}}">{{ $ticketcomment->user->name }}</a>
@if($ticketcomment->user->role === "member")
<span class="badge badge-secondary"> Member </span>
@elseif ($ticketcomment->user->role === "client")
<span class="badge badge-success"> Client </span>
@elseif ($ticketcomment->user->role === "mod")
<span class="badge badge-info"> Moderator </span>
@elseif ($ticketcomment->user->role === "admin")
<span class="badge badge-danger"> Admin </span>
@endif
</h5>
<span class="badge badge-primary">{{ $ticketcomment->created_at->diffForHumans() }}</span>
</div>
</div>
<div class="card-body">
{{ $ticketcomment->ticketcomment }}
</div>
</div>
@endforeach
<div class="comment-form">
<form action="{{ route('mod.ticket.reply')}}" method="POST" class="form">
{!! csrf_field() !!}
<input type="hidden" name="ticket_id" value="{{ $ticket->id }}">
<div class="form-group{{ $errors->has('ticketcomment') ? ' has-error' : '' }}">
<textarea rows="10" id="ticketcomment" class="form-control" name="ticketcomment"></textarea>
@if ($errors->has('ticketcomment'))
<span class="help-block">
<strong>{{ $errors->first('ticketcomment') }}</strong>
</span>
@endif
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary">Submit</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- END CONTENT -->
@endsection

View file

@ -0,0 +1,127 @@
@extends('layouts.main')
@section('content')
<!-- CONTENT HEADER -->
<section class="content-header">
<div class="container-fluid">
<div class="row mb-2">
<div class="col-sm-6">
<h1>{{ __('Ticket') }}</h1>
</div>
<div class="col-sm-6">
<ol class="breadcrumb float-sm-right">
<li class="breadcrumb-item"><a href="{{ route('home') }}">{{ __('Dashboard') }}</a></li>
<li class="breadcrumb-item"><a class="text-muted"
href="{{ route('ticket.index') }}">{{ __('Ticket') }}</a>
</li>
</ol>
</div>
</div>
</div>
</section>
<!-- END CONTENT HEADER -->
<!-- MAIN CONTENT -->
<section class="content">
<div class="container-fluid">
<form action="{{route('ticket.new.store')}}" method="POST">
@csrf
<div class="row">
<div class="col-lg-6">
<div class="card">
<div class="card-header">
<h5 class="card-title">
<i class="fas fa-money-check-alt mr-2"></i>{{__('Open a new ticket')}}
</h5>
</div>
<div class="card-body">
<div class="form-group col-sm-12 {{ $errors->has('title') ? ' has-error' : '' }}">
<label for="title" class="control-label">Title</label>
<input id="title" type="text" class="form-control" name="title" value="{{ old('title') }}">
@if ($errors->has('title'))
<span class="help-block">
<strong>{{ $errors->first('title') }}</strong>
</span>
@endif
</div>
@if ($servers->count() >= 1)
<div class="form-group col-sm-12 {{ $errors->has('server') ? ' has-error' : '' }}">
<label for="server" class="control-label">Servers</label>
<select id="server" type="server" class="form-control" name="server">
<option value="">Select Servers</option>
@foreach ($servers as $server)
<option value="{{ $server->id }}">{{ $server->name }}</option>
@endforeach
</select>
@if ($errors->has('category'))
<span class="help-block">
<strong>{{ $errors->first('ticketcategory') }}</strong>
</span>
@endif
</div>
@endif
<div class="form-group col-sm-12 {{ $errors->has('ticketcategory') ? ' has-error' : '' }}">
<label for="ticketcategory" class="control-label">Category</label>
<select id="ticketcategory" type="ticketcategory" class="form-control" name="ticketcategory">
<option value="">Select Category</option>
@foreach ($ticketcategories as $ticketcategory)
<option value="{{ $ticketcategory->id }}">{{ $ticketcategory->name }}</option>
@endforeach
</select>
@if ($errors->has('category'))
<span class="help-block">
<strong>{{ $errors->first('ticketcategory') }}</strong>
</span>
@endif
</div>
<div class="form-group col-sm-12 {{ $errors->has('priority') ? ' has-error' : '' }}">
<label for="priority" class="control-label">Priority</label>
<select id="priority" type="" class="form-control" name="priority">
<option value="">Select Priority</option>
<option value="Low">Low</option>
<option value="Medium">Medium</option>
<option value="High">High</option>
</select>
@if ($errors->has('priority'))
<span class="help-block">
<strong>{{ $errors->first('priority') }}</strong>
</span>
@endif
</div>
</div>
<div class="card-footer">
<button type="submit" class="btn btn-primary">
{{__('Open Ticket')}}
</button>
</div>
</div>
</div>
<div class="col-lg-6">
<div class="card">
<div class="card-header">
<h5 class="card-title">
<i class="fas fa-money-check-alt mr-2"></i>{{__('Ticket details')}}
</h5>
</div>
<div class="card-body">
<div class="form-group col-sm-12 {{ $errors->has('message') ? ' has-error' : '' }}">
<label for="message" class="control-label">Message</label>
<textarea rows="8" id="message" class="form-control" name="message"></textarea>
@if ($errors->has('message'))
<span class="help-block">
<strong>{{ $errors->first('message') }}</strong>
</span>
@endif
</div>
</div>
</div>
</div>
</div>
</form>
</div>
</section>
<!-- END CONTENT -->
@endsection

View file

@ -0,0 +1,100 @@
@extends('layouts.main')
@section('content')
<!-- CONTENT HEADER -->
<section class="content-header">
<div class="container-fluid">
<div class="row mb-2">
<div class="col-sm-6">
<h1>{{ __('Ticket') }}</h1>
</div>
<div class="col-sm-6">
<ol class="breadcrumb float-sm-right">
<li class="breadcrumb-item"><a href="{{ route('home') }}">{{ __('Dashboard') }}</a></li>
<li class="breadcrumb-item"><a class="text-muted"
href="{{ route('ticket.index') }}">{{ __('Ticket') }}</a>
</li>
</ol>
</div>
</div>
</div>
</section>
<!-- END CONTENT HEADER -->
<!-- MAIN CONTENT -->
<section class="content">
<div class="container-fluid">
<div class="row">
<div class="col-lg-8">
<div class="card">
<div class="card-header">
<div class="d-flex justify-content-between">
<h5 class="card-title"><i class="fas fa-ticket-alt mr-2"></i>{{__('My Ticket')}}</h5>
<a href="{{route('ticket.new')}}" class="btn btn-sm btn-primary"><i
class="fas fa-plus mr-1"></i>{{__('New Ticket')}}</a>
</div>
</div>
<div class="card-body table-responsive">
<table id="datatable" class="table table-striped">
<thead>
<tr>
<th>Category</th>
<th>Title</th>
<th>Status</th>
<th>Last Updated</th>
</tr>
</thead>
<tbody>
@foreach ($tickets as $ticket)
<tr>
<td>
{{ $ticket->ticketcategory->name }}
</td>
<td>
<a href="{{ route('ticket.show', ['ticket_id' => $ticket->ticket_id]) }}">
#{{ $ticket->ticket_id }} - {{ $ticket->title }}
</a>
</td>
<td>
@if ($ticket->status === 'Open')
<span class="badge badge-success">Open</span>
@elseif ($ticket->status === 'Closed')
<span class="badge badge-danger">Closed</span>
@elseif ($ticket->status === 'Answered')
<span class="badge badge-info">Answered</span>
@elseif ($ticket->status === 'Client Reply')
<span class="badge badge-warning">Client Reply</span>
@endif
</td>
<td>{{ $ticket->updated_at }}</td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
</div>
<div class="col-lg-4">
<div class="card">
<div class="card-header">
<h5 class="card-title">{{__('Ticket Information')}}
<i data-toggle="popover"
data-trigger="hover"
data-content="{{__('please make the best of it')}}"
class="fas fa-info-circle"></i></h5>
</div>
<div class="card-body">
<p>Can't start your server? Need an additional port? Do you have any other questions? Let us know by
opening a ticket.</p>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- END CONTENT -->
@endsection

View file

@ -0,0 +1,141 @@
@extends('layouts.main')
@section('content')
<!-- CONTENT HEADER -->
<section class="content-header">
<div class="container-fluid">
<div class="row mb-2">
<div class="col-sm-6">
<h1>{{ __('Ticket') }}</h1>
</div>
<div class="col-sm-6">
<ol class="breadcrumb float-sm-right">
<li class="breadcrumb-item"><a href="{{ route('home') }}">{{ __('Dashboard') }}</a></li>
<li class="breadcrumb-item"><a class="text-muted"
href="{{ route('ticket.index') }}">{{ __('Ticket') }}</a>
</li>
</ol>
</div>
</div>
</div>
</section>
<!-- END CONTENT HEADER -->
<!-- MAIN CONTENT -->
<section class="content">
<div class="container-fluid">
<div class="row">
<div class="col-lg-12">
<div class="card">
<div class="card-header">
<div class="d-flex justify-content-between">
<h5 class="card-title"><i class="fas fa-users mr-2"></i>#{{ $ticket->ticket_id }}</h5>
</div>
</div>
<div class="card-body">
<div class="ticket-info">
@if(!empty($server))
<p><b>Server:</b> <a href="{{ config('SETTINGS::SYSTEM:PTERODACTYL:URL') }}/server/{{ $server->identifier }}" target="__blank">{{ $server->name }} </a></p>
@endif
<p><b>Title:</b> {{ $ticket->title }}</p>
<p><b>Category:</b> {{ $ticketcategory->name }}</p>
<p>
@if ($ticket->status === 'Open')
<b>Status:</b> <span class="badge badge-success">Open</span>
@elseif ($ticket->status === 'Closed')
<b>Status:</b> <span class="badge badge-danger">Closed</span>
@elseif ($ticket->status === 'Answered')
<b>Status:</b> <span class="badge badge-info">Answered</span>
@elseif ($ticket->status === 'Client Reply')
<b>Status:</b> <span class="badge badge-warning">Client Reply</span>
@endif
</p>
<p><b>Created on:</b> {{ $ticket->created_at->diffForHumans() }}</p>
</div>
</div>
</div>
</div>
<div class="col-lg-12">
<div class="card">
<div class="card-header">
<div class="d-flex justify-content-between">
<h5 class="card-title"><i class="fas fa-cloud mr-2"></i>{{__('Comment')}}</h5>
</div>
</div>
<div class="card-body">
<div class="card">
<div class="card-header">
<div class="d-flex justify-content-between">
<h5 class="card-title"><img
src="https://www.gravatar.com/avatar/{{ md5(strtolower($ticket->user->email)) }}?s=25"
class="user-image" alt="User Image">
<a href="/admin/users/{{$ticket->user->id}}">{{ $ticket->user->name }} </a>
@if($ticket->user->role === "member")
<span class="badge badge-secondary"> Member </span>
@elseif ($ticket->user->role === "client")
<span class="badge badge-success"> Client </span>
@elseif ($ticket->user->role === "mod")
<span class="badge badge-info"> Moderator </span>
@elseif ($ticket->user->role === "admin")
<span class="badge badge-danger"> Admin </span>
@endif
</h5>
<span class="badge badge-primary">{{ $ticket->created_at->diffForHumans() }}</span>
</div>
</div>
<div class="card-body">
{{ $ticket->message }}
</div>
</div>
@foreach ($ticketcomments as $ticketcomment)
<div class="card">
<div class="card-header">
<div class="d-flex justify-content-between">
<h5 class="card-title"><img
src="https://www.gravatar.com/avatar/{{ md5(strtolower($ticketcomment->user->email)) }}?s=25"
class="user-image" alt="User Image">
<a href="/admin/users/{{$ticketcomment->user->id}}">{{ $ticketcomment->user->name }}</a>
@if($ticketcomment->user->role === "member")
<span class="badge badge-secondary"> Member </span>
@elseif ($ticketcomment->user->role === "client")
<span class="badge badge-success"> Client </span>
@elseif ($ticketcomment->user->role === "mod")
<span class="badge badge-info"> Moderator </span>
@elseif ($ticketcomment->user->role === "admin")
<span class="badge badge-danger"> Admin </span>
@endif
</h5>
<span class="badge badge-primary">{{ $ticketcomment->created_at->diffForHumans() }}</span>
</div>
</div>
<div class="card-body">
{{ $ticketcomment->ticketcomment }}
</div>
</div>
@endforeach
<div class="comment-form">
<form action="{{ route('ticket.reply')}}" method="POST" class="form">
{!! csrf_field() !!}
<input type="hidden" name="ticket_id" value="{{ $ticket->id }}">
<div class="form-group{{ $errors->has('ticketcomment') ? ' has-error' : '' }}">
<textarea rows="10" id="ticketcomment" class="form-control" name="ticketcomment"></textarea>
@if ($errors->has('ticketcomment'))
<span class="help-block">
<strong>{{ $errors->first('ticketcomment') }}</strong>
</span>
@endif
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary">Submit</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- END CONTENT -->
@endsection

View file

@ -14,6 +14,7 @@ use App\Http\Controllers\Admin\SettingsController;
use App\Http\Controllers\Admin\UsefulLinkController;
use App\Http\Controllers\Admin\UserController;
use App\Http\Controllers\Admin\VoucherController;
use App\Http\Controllers\Mod\TicketsController as ModTicketsController;
use App\Http\Controllers\Auth\SocialiteController;
use App\Http\Controllers\HomeController;
use App\Http\Controllers\NotificationController;
@ -22,6 +23,7 @@ use App\Http\Controllers\ProfileController;
use App\Http\Controllers\ServerController;
use App\Http\Controllers\StoreController;
use App\Http\Controllers\TranslationController;
use App\Http\Controllers\TicketsController;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Route;
@ -89,7 +91,13 @@ Route::middleware(['auth', 'checkSuspended'])->group(function () {
#switch language
Route::post('changelocale', [TranslationController::class, 'changeLocale'])->name('changeLocale');
#ticket user
Route::get('ticket', [TicketsController::class, 'index'])->name('ticket.index');
Route::get('ticket/new', [TicketsController::class, 'create'])->name('ticket.new');
Route::post('ticket/new', [TicketsController::class, 'store'])->name('ticket.new.store');
Route::get('ticket/show/{ticket_id}', [TicketsController::class, 'show'])->name('ticket.show');
Route::post('ticket/reply', [TicketsController::class, 'reply'])->name('ticket.reply');
#admin
Route::prefix('admin')->name('admin.')->middleware('admin')->group(function () {
@ -164,5 +172,15 @@ Route::middleware(['auth', 'checkSuspended'])->group(function () {
]);
});
#mod
Route::prefix('mod')->name('mod.')->middleware('mod')->group(function () {
#ticket moderation
Route::get('ticket', [ModTicketsController::class, 'index'])->name('ticket.index');
Route::get('ticket/show/{ticket_id}', [ModTicketsController::class, 'show'])->name('ticket.show');
Route::post('ticket/reply', [ModTicketsController::class, 'reply'])->name('ticket.reply');
Route::post('ticket/close/{ticket_id}', [ModTicketsController::class, 'close'])->name('ticket.close');
Route::post('ticket/delete/{ticket_id}', [ModTicketsController::class, 'delete'])->name('ticket.delete');
});
Route::get('/home', [HomeController::class, 'index'])->name('home');
});