Remove files related to V2 admin panel

This commit is contained in:
Ralph J. Smit
2022-08-06 17:24:24 +02:00
parent 60c951a1f8
commit 30685c4595
74 changed files with 0 additions and 7706 deletions

View File

@@ -1,80 +0,0 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Models\Alert;
use App\Http\Controllers\Controller;
use App\Http\Requests\Admin\AlertRequest;
class AlertController extends Controller
{
public function index()
{
return inertia('Admin/Alerts/Index', [
'alerts' => Alert::query()->latest()->paginate()
]);
}
public function create()
{
return inertia('Admin/Alerts/Create');
}
public function store(AlertRequest $request)
{
Alert::create($request->all());
return redirect()->route('admin.alerts.index');
}
/**
* Display the specified resource.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function show($id)
{
//
}
/**
* Show the form for editing the specified resource.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function edit($id)
{
return inertia('Admin/Alerts/Edit', [
'alert' => Alert::findOrFail($id)
]);
}
/**
* Update the specified resource in storage.
*
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\Http\Response
*/
public function update(AlertRequest $request, $id)
{
Alert::findOrFail($id)->update($request->all());
return redirect()->route('admin.alerts.index');
}
/**
* Remove the specified resource from storage.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function destroy($id)
{
Alert::findOrFail($id)->delete();
return redirect()->route('admin.alerts.index');
}
}

View File

@@ -1,98 +0,0 @@
<?php
namespace App\Http\Controllers\Admin;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
class ApplicationLogController extends Controller
{
protected $final = [];
protected $config = [
'date' => null
];
public function index(Request $request)
{
$this->config['date'] = date('Y-m-d');
if ($date = $request->input('date')) {
$this->config['date'] = $date;
} else {
$request->merge(['date' => date('Y-m-d')]);
}
return inertia('Admin/ApplicationLogs', [
'logData' => $this->get(),
'filters' => $request->all('date')
]);
}
public function getLogFileDates()
{
$dates = [];
$files = glob(storage_path('logs/laravel-*.log'));
$files = array_reverse($files);
foreach ($files as $path) {
$fileName = basename($path);
preg_match('/(?<=laravel-)(.*)(?=.log)/', $fileName, $dtMatch);
$date = $dtMatch[0];
array_push($dates, $date);
}
return $dates;
}
public function get()
{
$availableDates = $this->getLogFileDates();
if (count($availableDates) == 0) {
return response()->json([
'success' => false,
'message' => 'No log available'
]);
}
$configDate = $this->config['date'];
if ($configDate == null) {
$configDate = $availableDates[0];
}
if (!in_array($configDate, $availableDates)) {
return response()->json([
'success' => false,
'message' => 'No log file found with selected date ' . $configDate
]);
}
$pattern = "/^\[(?<date>.*)\]\s(?<env>\w+)\.(?<type>\w+):(?<message>.*)/m";
$fileName = 'laravel-' . $configDate . '.log';
$content = file_get_contents(storage_path('logs/' . $fileName));
preg_match_all($pattern, $content, $matches, PREG_SET_ORDER, 0);
$logs = [];
foreach ($matches as $match) {
$logs[] = [
'timestamp' => $match['date'],
'env' => $match['env'],
'type' => $match['type'],
'message' => trim($match['message'])
];
}
preg_match('/(?<=laravel-)(.*)(?=.log)/', $fileName, $dtMatch);
$date = $dtMatch[0];
$data = [
'available_dates' => $availableDates,
'date' => $date,
'filename' => $fileName,
'logs' => array_reverse($logs)
];
return $data;
}
}

View File

@@ -1,36 +0,0 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Models\Server;
use App\Models\Site;
use App\Models\SystemLog;
use App\Models\User;
class DashboardController extends Controller
{
public function __invoke()
{
return inertia('Admin/Dashboard', [
'servers' => Server::count(),
'sites' => Site::count(),
'users' => User::count(),
'logs' => SystemLog::query()
->latest()
->with('model')
->paginate(5)
->through(function (SystemLog $systemLog) {
return [
'title' => __($systemLog->title, [
'site' => $systemLog->model->domain ?? '-Unknown-',
]),
'description' => __($systemLog->description, [
'site' => $systemLog->model->domain ?? '-Unknown-',
]),
'created_at_huma≥n' => $systemLog->created_at->diffForHumans(),
];
}),
]);
}
}

View File

@@ -1,69 +0,0 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Models\DocumentationItem;
use App\Http\Controllers\Controller;
use App\Models\DocumentationCategory;
use App\Http\Requests\Admin\DocumentationArticleRequest;
class DocumentationArticleController extends Controller
{
public function index()
{
$articles = DocumentationItem::query()->with('category:id,title')->latest()->paginate();
return inertia('Admin/Documentation/Articles/Index', [
'articles' => $articles
]);
}
public function create()
{
$categories = DocumentationCategory::pluck('title', 'id');
return inertia('Admin/Documentation/Articles/Create', [
'categories' => $categories,
]);
}
public function store(DocumentationArticleRequest $request)
{
$article = DocumentationItem::create([
'title' => $request->input('title'),
'content' => $request->input('content')
]);
$article->documentation_category_id = $request->input('category_id');
$article->save();
return redirect()->route('admin.documentation.articles.index')->with('success', __('Documentation article has been created'));
}
public function edit($id)
{
$article = DocumentationItem::findOrFail($id);
$categories = DocumentationCategory::pluck('title', 'id');
return inertia('Admin/Documentation/Articles/Edit', [
'article' => $article,
'categories' => $categories
]);
}
public function update(DocumentationArticleRequest $request, $id)
{
$article = DocumentationItem::findOrFail($id);
$article->update([
'title' => $request->input('title'),
'content' => $request->input('content')
]);
$article->documentation_category_id = $request->input('category_id');
$article->save();
return redirect()->route('admin.documentation.articles.index')->with('success', __('Documentation article has been updated'));
}
}

View File

@@ -1,55 +0,0 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Models\DocumentationCategory;
use App\Http\Requests\Admin\DocumentationCategoryRequest;
class DocumentationController extends Controller
{
public function index()
{
$categories = DocumentationCategory::query()->latest()->paginate();
return inertia('Admin/Documentation/Index', [
'categories' => $categories
]);
}
public function create()
{
return inertia('Admin/Documentation/Create');
}
public function store(DocumentationCategoryRequest $request)
{
DocumentationCategory::create([
'title' => $request->input('title'),
'description' => $request->input('description')
]);
return redirect()->route('admin.documentation.index')->with('success', __('Documentation category has been created'));
}
public function edit($id)
{
$category = DocumentationCategory::findOrFail($id);
return inertia('Admin/Documentation/Edit', [
'category' => $category
]);
}
public function update(DocumentationCategoryRequest $request, $id)
{
$category = DocumentationCategory::findOrFail($id);
$category->update([
'title' => $request->input('title'),
'description' => $request->input('description'),
]);
return redirect()->route('admin.documentation.index')->with('success', __('Documentation category has been updated'));
}
}

View File

@@ -1,71 +0,0 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Models\Package;
use App\Models\Provider;
use App\Http\Controllers\Controller;
use App\Http\Requests\Admin\PackageRequest;
class PackageController extends Controller
{
public function index()
{
$packages = Package::withCount('users')->latest()->get();
return inertia('Admin/Packages/Index', [
'packages' => $packages,
]);
}
public function create()
{
$providers = Provider::get(['name', 'label', 'id'])->pluck('nameWithLabel', 'id');
return inertia('Admin/Packages/Create', [
'providers' => $providers
]);
}
public function store(PackageRequest $request)
{
$package = Package::create($request->validated());
$package->providers()->sync($request->input('providers'));
return redirect()->route('admin.packages.index')->with('success', __('Package has been created'));
}
public function edit($id)
{
$package = Package::with('providers:id')->findOrFail($id);
$providers = Provider::get(['name', 'label', 'id'])->pluck('nameWithLabel', 'id');
return inertia('Admin/Packages/Edit', [
'package' => $package,
'providers' => $providers,
'syncedProviders' => $package->providers->pluck('id')
]);
}
public function update(PackageRequest $request, $id)
{
$package = Package::findOrFail($id);
$package->update($request->validated());
$package->providers()->sync($request->input('providers'));
return redirect()->route('admin.packages.index')->with('success', __('Package has been updated'));
}
public function destroy($id)
{
$package = Package::findOrFail($id);
$package->delete();
return redirect()->route('admin.packages.index')->with('success', __('Package has been removed'));
}
}

View File

@@ -1,39 +0,0 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Models\Provider;
use App\Http\Controllers\Controller;
use App\Http\Requests\Admin\ProviderRequest;
class ProviderController extends Controller
{
public function edit($id)
{
$provider = Provider::findOrFail($id);
return inertia('Admin/Services/Provider/Edit', [
'provider' => $provider,
'availablePlans' => $provider->plans()->pluck('label', 'id'),
'availableRegions' => $provider->regions()->pluck('label', 'id'),
]);
}
public function update(ProviderRequest $request, $id)
{
$provider = Provider::findOrFail($id);
$provider->update($request->validated());
return redirect()->route('admin.services.index')->with('success', __('Provider has been updated'));
}
public function destroy($id)
{
$provider = Provider::findOrFail($id);
$provider->delete();
return redirect()->route('admin.services.index')->with('success', __('Provider has been deleted'));
}
}

View File

@@ -1,10 +0,0 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
class ProviderPlanController extends Controller
{
//
}

View File

@@ -1,10 +0,0 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
class ProviderRegionController extends Controller
{
//
}

View File

@@ -1,102 +0,0 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Models\User;
use App\Models\Server;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use App\Http\Resources\Admin\ServerResource;
use App\Http\Requests\Admin\ServerAttachRequest;
class ServerController extends Controller
{
public function index()
{
return inertia('Admin/Servers/Index', [
'filters' => request()->all('search'),
'servers' => ServerResource::collection(
Server::query()
->when(request()->input('search'), function (Builder $query, $value) {
return $query
->where('name', 'like', '%' . $value . '%')
->orWhere('ip', 'like', '%' . $value . '%')
->orWhereHas('users', function (Builder $query) use ($value) {
return $query
->where('name', 'LIKE', '%' . $value . '%')
->orWhere('email', 'LIKE', '%' . $value . '%');
});
})
->with('users:id,name')
->withCount('sites')
->latest()
->paginate(config('core.pagination.per_page'))
->withQueryString()
)
]);
}
public function edit($id)
{
$server = Server::findOrFail($id);
$users = $server->users()->select('id', 'name', 'email')->get()->map(function ($user) {
return [
'id' => $user->id,
'name' => $user->name,
'email' => $user->email
];
});
return inertia('Admin/Services/Server/Edit', [
'server' => $server,
'users' => $users
]);
}
public function update(Request $request, $id)
{
$server = Server::findOrFail($id);
$server->update($request->all());
return redirect()->route('admin.services.index')->with('success', __('Server has been updated'));
}
public function destroy($id)
{
$server = Server::findOrFail($id);
$server->delete();
return redirect()->route('admin.services.index')->with('success', __('Server has been deleted'));
}
public function attach(ServerAttachRequest $request, $id)
{
/* @var $server \App\Models\Server */
$server = Server::findOrFail($id);
$user = User::where('email', $request->input('email'))->first();
if ($server->users()->where('email', $request->input('email'))->count()) {
return redirect()->back()->withErrors([
'email' => __('This user is already attached to this server')
]);
}
$server->users()->attach($user);
return redirect()->route('admin.services.servers.edit', $id)->with('success', __('User has been attached'));
}
public function detach($id, $userId)
{
$server = Server::findOrFail($id);
$server->users()->detach($userId);
return redirect()->route('admin.services.servers.edit', $server->id)->with('success', __('User has been detached'));
}
}

View File

@@ -1,23 +0,0 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Models\Site;
use App\Models\Server;
use App\Models\Provider;
use App\Http\Controllers\Controller;
class ServiceController extends Controller
{
public function index()
{
return inertia('Admin/Services/Index', [
'servers' => Server::query()->withCount('sites', 'users')->latest()->paginate(config('core.pagination.per_page'), ['*'], 'servers_per_page'),
'sites' => Site::with('server:id,name')->withCount('users')->latest()->paginate(config('core.pagination.per_page'), ['*'], 'sites_per_page'),
'providers' => Provider::query()
->withCount('regions', 'plans', 'servers')
->latest()
->paginate(config('core.pagination.per_page'), ['*'], 'providers_per_page'),
]);
}
}

View File

@@ -1,147 +0,0 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Models\Package;
use Illuminate\Support\Str;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Storage;
use App\Http\Requests\Admin\SettingRequest;
class SettingController extends Controller
{
public function index()
{
$settings = [
'name' => setting('name'),
'email' => setting('email'),
'support_emails' => setting('support_emails'),
'support' => setting('support'),
'documentation' => setting('documentation'),
'allow_registration' => setting('allow_registration'),
'default_package' => setting('default_package'),
'receive_email_on_server_creation' => setting('receive_email_on_server_creation'),
'receive_email_on_site_creation' => setting('receive_email_on_site_creation'),
'isolate_per_site_per_user' => setting('isolate_per_site_per_user'),
'enable_api' => setting('enable_api'),
'api_token' => setting('api_token') ? decrypt(setting('api_token')) : null,
'rotate_logs_after' => setting('rotate_logs_after') ? setting('rotate_logs_after') : null,
'default_language' => setting('default_language', 'en'),
'has_logo' => (bool)setting('logo'),
'trial' => setting('trial'),
'trial_package' => setting('trial_package'),
];
$packages = Package::pluck('name', 'id');
return inertia('Admin/Settings', [
'company_settings' => $settings,
'packages' => $packages,
'languages' => languages()
]);
}
public function update(SettingRequest $request)
{
foreach ($request->only([
'name',
'email',
'support',
'support_emails',
'allow_registration',
'documentation',
'default_package',
'receive_email_on_server_creation',
'receive_email_on_site_creation',
'isolate_per_site_per_user',
'enable_api',
'api_token',
'default_language',
'rotate_logs_after',
'trial',
'trial_package'
]) as $key => $value) {
if ($key === 'api_token') {
$value = encrypt($value);
}
if ($key === 'default_package' && $value === 'false') {
$value = null;
}
if ($value === 'false') {
$value = 0;
}
if ($value === 'true') {
$value = 1;
}
setting([$key => $value]);
}
if ($logo = $request->file('logo')) {
// If we previously had a logo, rotate that
if ($oldLogo = setting('logo')) {
Storage::delete(str_replace('/storage/', '/public/', $oldLogo));
}
$version = Str::random();
$request->file('logo')->storePubliclyAs('logo', 'logo-' . $version . '.' . $request->file('logo')->extension(), 'public');
setting(['logo' => '/storage/logo/logo-' . $version . '.' . $request->file('logo')->extension()]);
}
cache()->forget('core.settings');
return redirect()->route('admin.settings')->with('success', __('Settings have been updated'));
}
public function terms()
{
return inertia('Admin/Terms', [
'terms_settings' => [
'logo' => setting('logo'),
'name' => setting('name'),
'terms_required' => setting('accept_terms_required'),
'terms' => setting('terms'),
'privacy' => setting('privacy')
]
]);
}
public function updateTerms(Request $request)
{
setting(['accept_terms_required' => $request->input('terms_required'),]);
setting(['terms' => $request->input('terms'),]);
setting(['privacy' => $request->input('privacy'),]);
return redirect()->route('admin.settings.terms')->with('success', __('Terms have been updated'));
}
public function termsTemplate(Request $request)
{
$template = file_get_contents(storage_path('templates/terms-of-service.md'));
$template = str_replace([
'{NAME}',
'{WEBSITE}',
'{DATE}'
], [
setting('name'),
config('app.url'),
date('Y-m-d')
], $template);
return ['content' => $template];
}
public function removeLogo(Request $request)
{
Storage::delete(setting('logo'));
setting(['logo' => null]);
return redirect()->back()->with('success', 'Logo has ben removed');
}
}

View File

@@ -1,94 +0,0 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Http\Requests\Admin\ServerAttachRequest;
use App\Http\Resources\Admin\SiteResource;
use App\Models\Server;
use App\Models\Site;
use App\Models\User;
use Illuminate\Http\Request;
class SiteController extends Controller
{
public function index()
{
return inertia('Admin/Sites/Index', [
'filters' => request()->all('search'),
'sites' => SiteResource::collection(
Site::query()
->when(request()->input('search'), function ($query, $value) {
return $query->where('domain', 'like', '%' . $value . '%');
})
->with('server:id,name', 'users:id,name')
->latest()
->paginate(config('core.pagination.per_page'))
->withQueryString()
),
]);
}
public function edit($id)
{
$site = Site::findOrFail($id);
$users = $site->users()->select('id', 'name', 'email')->get()->map(function ($user) {
return [
'id' => $user->id,
'name' => $user->name,
'email' => $user->email,
];
});
return inertia('Admin/Services/Site/Edit', [
'site' => $site,
'users' => $users,
]);
}
public function update(Request $request, $id)
{
$site = Site::findOrFail($id);
$site->update($request->all());
return redirect()->route('admin.services.index')->with('success', __('Site has been updated'));
}
public function destroy($id)
{
$site = Site::findOrFail($id);
$site->delete();
return redirect()->route('admin.services.index')->with('success', __('Site has been deleted'));
}
public function attach(ServerAttachRequest $request, $id)
{
/* @var $site Server */
$site = Site::findOrFail($id);
$user = User::where('email', $request->input('email'))->first();
if ( $site->users()->where('email', $request->input('email'))->count() ) {
return redirect()->back()->withErrors([
'email' => __('This user is already attached to this site'),
]);
}
$site->users()->attach($user);
return redirect()->route('admin.services.sites.edit', $id)->with('success', __('User has been attached'));
}
public function detach($id, $userId)
{
$site = Site::findOrFail($id);
$site->users()->detach($userId);
return redirect()->route('admin.services.sites.edit', $site->id)->with('success', __('User has been detached'));
}
}

View File

@@ -1,13 +0,0 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
class StatusController extends Controller
{
public function index()
{
return inertia('Admin/Status');
}
}

View File

@@ -1,71 +0,0 @@
<?php
namespace App\Http\Controllers\Admin;
use Illuminate\Http\Request;
use App\Models\SupportTicket;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Mail;
use App\Mail\Support\TicketRepliedToEmail;
use App\Http\Requests\SupportTicketReplyRequest;
class SupportController extends Controller
{
public function index()
{
$tickets = SupportTicket::whereIn('status', [
SupportTicket::STATUS_CUSTOMER_REPLY,
SupportTicket::STATUS_OPEN
])
->withCount('replies')
->latest()
->get();
return inertia('Admin/Support/Index', [
'tickets' => $tickets
]);
}
public function show($id)
{
$ticket = SupportTicket::with('user:id,name,email')->findOrFail($id);
$replies = $ticket->replies()
->with('user:id,name,email')
->get();
return inertia('Admin/Support/Show', [
'ticket' => $ticket,
'replies' => $replies
]);
}
public function reply(SupportTicketReplyRequest $request, $id)
{
$ticket = SupportTicket::findOrFail($id);
$ticket->status = SupportTicket::STATUS_SUPPORT_REPLY;
$ticket->save();
$reply = $ticket->replies()->create([
'content' => $request->input('content')
]);
$reply->user_id = $request->user()->id;
$reply->save();
Mail::to($ticket->user)->send(new TicketRepliedToEmail($ticket));
return redirect()->route('admin.support.index');
}
public function close(Request $request, $id)
{
$ticket = SupportTicket::findOrFail($id);
$ticket->status = SupportTicket::STATUS_CLOSED;
$ticket->save();
return redirect()->route('admin.support.index')->with('success', __('Support ticket has been closed'));
}
}

View File

@@ -1,56 +0,0 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Models\Provider;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
class SynchronizeProviderController extends Controller
{
public function index()
{
if ($this->isDemo()) {
return redirect('/')->with('info', __('This feature is not available in demo mode.'));
}
$availableProviders = $this->getPloi()->user()->serverProviders()->getData();
$currentProviders = Provider::query()
->whereNotIn('id', array_keys((array)$availableProviders))
->get();
return inertia('Admin/Services/Providers', [
'availableProviders' => $availableProviders,
'currentProviders' => $currentProviders
]);
}
public function synchronize(Request $request, $providerId)
{
$ploiProvider = $this->getPloi()->user()->serverProviders($providerId)->getData();
$provider = Provider::updateOrCreate([
'ploi_id' => $ploiProvider->id,
], [
'label' => $ploiProvider->label,
'name' => $ploiProvider->name
]);
foreach ($ploiProvider->provider->plans as $plan) {
$provider->plans()->updateOrCreate([
'plan_id' => $plan->id
], [
'label' => $plan->name,
]);
}
foreach ($ploiProvider->provider->regions as $region) {
$provider->regions()->updateOrCreate([
'region_id' => $region->id
], [
'label' => $region->name,
]);
}
}
}

View File

@@ -1,65 +0,0 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Models\Server;
use Illuminate\Http\Request;
class SynchronizeServerController extends Controller
{
public function index()
{
if ( $this->isDemo() ) {
return redirect('/')->with('info', __('This feature is not available in demo mode.'));
}
$availableServers = $this->getPloi()->synchronize()->servers()->getData();
$currentServers = Server::query()
->whereNotIn('id', array_keys((array) $availableServers))
->get();
return inertia('Admin/Services/Servers', [
'availableServers' => $availableServers,
'currentServers' => $currentServers,
]);
}
public function synchronizeServer(Request $request)
{
$this->getPloi()->synchronize()->servers()->getData();
Server::query()
->updateOrCreate([
'ploi_id' => $request->input('id'),
], [
'status' => $request->input('status'),
'name' => $request->input('name'),
'ip' => $request->input('ip_address'),
'ssh_port' => $request->input('ssh_port', 22),
'internal_ip' => $request->input('internal_ip'),
'available_php_versions' => $request->input('installed_php_versions'),
]);
}
public function synchronizeAll()
{
$availableServers = $this->getPloi()->synchronize()->servers()->getData();
foreach ($availableServers as $availableServer) {
Server::query()
->updateOrCreate([
'ploi_id' => $availableServer->id,
], [
'status' => $availableServer->status,
'name' => $availableServer->name,
'ip' => $availableServer->ip_address,
'ssh_port' => $availableServer->ssh_port,
'internal_ip' => $availableServer->internal_ip,
'available_php_versions' => $availableServer->installed_php_versions,
]);
}
return response('ok');
}
}

View File

@@ -1,87 +0,0 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Models\Server;
use App\Models\Site;
use Illuminate\Http\Request;
class SynchronizeSiteController extends Controller
{
public function index()
{
if ( $this->isDemo() ) {
return redirect('/')->with('info', __('This feature is not available in demo mode.'));
}
$availableSites = $this->getPloi()->synchronize()->sites()->getData();
$currentSites = Site::query()
->whereNotIn('id', array_keys((array) $availableSites))
->get();
return inertia('Admin/Services/Sites', [
'availableSites' => $availableSites,
'currentSites' => $currentSites,
]);
}
public function synchronizeSite(Request $request)
{
$server = Server::query()
->where('ploi_id', $request->input('server_id'))
->firstOrFail();
$site = Site::query()
->updateOrCreate([
'ploi_id' => $request->input('id'),
], [
'domain' => $request->input('domain'),
'php_version' => $request->input('php_version'),
'project' => $request->input('project_type'),
]);
$site->status = $request->input('status');
$site->server_id = $server->id;
$site->save();
$certificates = $this->getPloi()->server($request->input('server_id'))->sites($request->input('id'))->certificates()->get()->getData();
if ( $certificates ) {
foreach ($certificates as $certificate) {
$site->certificates()->updateOrCreate([
'ploi_id' => $certificate->id,
], [
'status' => $certificate->status,
'ploi_id' => $certificate->id,
'domain' => $certificate->domain,
'type' => $certificate->type,
]);
}
}
return response('ok');
}
public function synchronizeAll(Request $request)
{
$availableSites = $this->getPloi()->synchronize()->sites()->getData();
foreach ($availableSites as $availableSite) {
$server = Server::query()->where('ploi_id', $availableSite->server_id)->firstOrFail();
$site = Site::query()
->updateOrCreate([
'ploi_id' => $availableSite->id,
], [
'domain' => $availableSite->domain,
'php_version' => $availableSite->php_version,
]);
$site->status = $availableSite->status;
$site->server_id = $server->id;
$site->save();
}
}
}

View File

@@ -1,41 +0,0 @@
<?php
namespace App\Http\Controllers\Admin;
use Inertia\Response;
use Illuminate\Http\Request;
use App\Jobs\Core\UpdateSystem;
use App\Services\VersionChecker;
use App\Http\Controllers\Controller;
use Illuminate\Http\RedirectResponse;
use Laravel\Horizon\Contracts\MasterSupervisorRepository;
class SystemController extends Controller
{
public function index(Request $request, MasterSupervisorRepository $masterSupervisorRepository): Response|RedirectResponse
{
if ($request->input('flush', false)) {
app(VersionChecker::class)->flushVersionData();
return redirect()->route('admin.system')->with('success', __('Refreshed versions'));
}
$version = app(VersionChecker::class)->getVersions();
return inertia('Admin/System', [
'version' => [
'out_of_date' => $version->isOutOfDate(),
'current' => $version->currentVersion,
'remote' => $version->remoteVersion
],
'horizonRunning' => (bool) $masterSupervisorRepository->all(),
]);
}
public function update(Request $request): RedirectResponse
{
dispatch(new UpdateSystem);
return redirect()->route('admin.system')->with('success', __('System update has been dispatched, this could take around 2/3 minutes for it to complete'));
}
}

View File

@@ -1,110 +0,0 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Http\Requests\Admin\UserRequest;
use App\Models\Package;
use App\Models\User;
class UserController extends Controller
{
public function index()
{
$users = User::query()
->withCount('sites', 'servers')
->with('package:id,name')
->when(request()->input('search'), function ($query, $value) {
return $query->where('name', 'like', '%' . $value . '%')->orWhere('email', 'like', '%' . $value . '%');
})
->latest()
->paginate(config('core.pagination.per_page'));
return inertia('Admin/Users/Index', [
'filters' => request()->all('search'),
'users' => $users,
]);
}
public function create()
{
$packages = Package::orderBy('name')
->pluck('name', 'id');
return inertia('Admin/Users/Create', [
'packages' => $packages,
'languages' => languages(),
'defaultPackage' => (string) setting('default_package'),
'defaultLanguage' => (string) setting('default_language', 'en'),
]);
}
public function store(UserRequest $request)
{
$user = User::create($request->all());
if ( $request->input('role') === User::ADMIN ) {
$user->role = User::ADMIN;
$user->save();
}
if ( $request->input('package') && Package::find($request->input('package')) ) {
$user->package_id = $request->input('package');
$user->save();
}
return redirect()->route('admin.users.index')->with('success', 'User ' . $user->name . ' has been created');
}
public function show($id)
{
$user = User::query()->findOrFail($id);
$servers = $user->servers()->withCount('sites')->latest()->paginate(config('core.pagination.per_page'), ['*'], 'page_servers');
$sites = $user->sites()->with('server:id,name')->latest()->paginate(config('core.pagination.per_page'), ['*'], 'page_sites');
return inertia('Admin/Users/Show', [
'user' => $user,
'sites' => $sites,
'servers' => $servers,
]);
}
public function edit($id)
{
$packages = Package::orderBy('name')->pluck('name', 'id');
return inertia('Admin/Users/Edit', [
'user' => User::findOrFail($id),
'packages' => $packages,
'languages' => languages(),
]);
}
public function update(UserRequest $request, $id)
{
$user = User::findOrFail($id);
$user->update($request->all());
if ( $request->input('role') !== $user->role ) {
$user->role = $request->input('role');
$user->save();
}
if ( $request->input('package') !== $user->package_id ) {
$user->package_id = $request->input('package');
$user->save();
}
return redirect()->route('admin.users.index')->with('success', 'User ' . $user->name . ' has been updated');
}
public function destroy($id)
{
User::findOrFail($id)->delete();
return redirect()->route('admin.users.index')->with('success', 'User has been deleted');
}
}

View File

@@ -1,50 +0,0 @@
<?php
namespace App\Http\Requests\Admin;
use App\Models\Alert;
use Illuminate\Validation\Rule;
use Illuminate\Foundation\Http\FormRequest;
class AlertRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return auth()->check() && auth()->user()->isAdmin();
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'message' => [
'required',
'string',
'max:500'
],
'type' => [
'required',
'string',
Rule::in([
Alert::TYPE_INFO,
Alert::TYPE_DANGER,
Alert::TYPE_WARNING
])
],
'expires_at' => [
'nullable',
'date',
'date_format:Y-m-d H:i:s'
]
];
}
}

View File

@@ -1,42 +0,0 @@
<?php
namespace App\Http\Requests\Admin;
use Illuminate\Foundation\Http\FormRequest;
class DocumentationArticleRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return auth()->check() && auth()->user()->isAdmin();
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'title' => [
'required',
'string',
'max:255'
],
'content' => [
'required',
'min:2'
],
'category_id' => [
'required',
'exists:documentation_categories,id'
]
];
}
}

View File

@@ -1,38 +0,0 @@
<?php
namespace App\Http\Requests\Admin;
use Illuminate\Foundation\Http\FormRequest;
class DocumentationCategoryRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return auth()->check() && auth()->user()->isAdmin();
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'title' => [
'required',
'string',
'max:255'
],
'description' => [
'nullable',
'string'
]
];
}
}

View File

@@ -1,98 +0,0 @@
<?php
namespace App\Http\Requests\Admin;
use App\Models\Package;
use Illuminate\Validation\Rule;
use Illuminate\Foundation\Http\FormRequest;
class PackageRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return auth()->check() && auth()->user()->isAdmin();
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'name' => [
'required',
'string',
'max:255'
],
'currency' => [
'nullable',
Rule::in([
Package::CURRENCY_USD,
Package::CURRENCY_EURO,
Package::CURRENCY_NOK,
Package::CURRENCY_AUD,
Package::CURRENCY_CAD,
Package::CURRENCY_GBP,
Package::CURRENCY_INR,
Package::CURRENCY_THB,
Package::CURRENCY_BRL,
])
],
'maximum_sites' => [
'required',
'numeric',
'min:0',
],
'maximum_servers' => [
'required',
'numeric',
'min:0',
],
'plan_id' => [
'nullable',
],
'price_monthly' => [
'nullable',
'numeric',
],
'price_yearly' => [
'nullable',
'numeric',
],
'server_permissions' => [
'array'
],
'site_permissions' => [
'array'
]
];
}
protected function prepareForValidation()
{
$merge = [];
// If we don't have the monthly price filled in, merge a default
if (!$this->price_monthly) {
$merge['price_monthly'] = 0.000;
}
if (!$this->price_yearly) {
$merge['price_yearly'] = 0.000;
}
// If we don't have the currency filled in, merge a default
if (!$this->price_monthly || !$this->price_yearly) {
$merge['currency'] = Package::CURRENCY_USD;
}
$this->merge($merge);
}
}

View File

@@ -1,42 +0,0 @@
<?php
namespace App\Http\Requests\Admin;
use Illuminate\Foundation\Http\FormRequest;
class ProviderRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return auth()->check() && auth()->user()->isAdmin();
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'name' => [
'required',
'string',
'max:255'
],
'allowed_plans' => [
'nullable',
'array'
],
'allowed_regions' => [
'nullable',
'array'
]
];
}
}

View File

@@ -1,34 +0,0 @@
<?php
namespace App\Http\Requests\Admin;
use Illuminate\Foundation\Http\FormRequest;
class ServerAttachRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return auth()->check() && auth()->user()->isAdmin();
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'email' => [
'required',
'email',
'exists:users'
]
];
}
}

View File

@@ -1,49 +0,0 @@
<?php
namespace App\Http\Requests\Admin;
use Illuminate\Foundation\Http\FormRequest;
class SettingRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return auth()->check() && auth()->user()->isAdmin();
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'name' => [
'required',
'string',
'max:255'
],
'email' => [
'nullable',
'email'
],
'logo' => [
'nullable',
'image',
'max:2000'
],
'trial_package' => [
'required_with:trial'
]
];
}
}

View File

@@ -1,53 +0,0 @@
<?php
namespace App\Http\Requests\Admin;
use App\Models\User;
use Illuminate\Validation\Rule;
use Illuminate\Foundation\Http\FormRequest;
class UserRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return auth()->check() && auth()->user()->isAdmin();
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'name' => [
'required',
'string',
'max:255'
],
'email' => [
'required',
'email:rfc,filter',
'max:255',
],
'role' => [
'required',
Rule::in([
User::ADMIN,
User::USER,
])
],
'language' => [
'required',
'string',
Rule::in(languages()),
]
];
}
}

View File

@@ -1,21 +0,0 @@
<?php
namespace App\Http\Resources\Admin;
use Illuminate\Http\Resources\Json\JsonResource;
class ServerResource extends JsonResource
{
public function toArray($request)
{
return [
'id' => $this->id,
'name' => $this->name,
'ip' => $this->ip,
'users' => $this->users,
'sites_count' => $this->sites_count,
'maximum_sites' => $this->maximum_sites,
'created_at' => $this->created_at->format('Y-m-d H:i:s')
];
}
}

View File

@@ -1,13 +0,0 @@
<?php
namespace App\Http\Resources\Admin;
use Illuminate\Http\Resources\Json\JsonResource;
class SiteResource extends JsonResource
{
public function toArray($request)
{
return parent::toArray($request);
}
}

View File

@@ -48,12 +48,6 @@ class RouteServiceProvider extends ServiceProvider
Route::middleware('web')
->namespace($this->namespace)
->group(base_path('routes/web.php'));
//
Route::middleware(['web', 'auth', 'role:admin'])
->prefix('admin-v2')
->as('admin.')
->namespace($this->namespace . '\\Admin')
->group(base_path('routes/admin.php'));
});
}

View File

@@ -1,131 +0,0 @@
<template>
<Page>
<TopBar/>
<Content>
<Container>
<PageHeader>
<template #start>
<PageHeaderTitle>{{ __('Create alert') }}</PageHeaderTitle>
</template>
</PageHeader>
<PageBody>
<SettingsLayout>
<template #nav>
<Tabs/>
</template>
<template #segments>
<SettingsSegment>
<template #title>{{ __('Create') }}</template>
<template #subtitle>
{{
__('Create a new alert here to notify your end users about a specific event. This could be anything from server maintenance to panel updates.')
}}
</template>
<template #form>
<form class="space-y-4" @submit.prevent="submit">
<FormTextarea :label="__('Message')" :errors="$page.props.errors.message"
v-model="form.message"/>
<FormInput :label="__('Expires at')" placeholder="2020-01-01 00:00:00" :errors="$page.props.errors.expires_at" v-model="form.expires_at"/>
<FormSelect :errors="$page.props.errors.type" :label="__('Type')" v-model="form.type">
<option value="info">{{ __('Informational') }}</option>
<option value="warning">{{ __('Warning') }}</option>
<option value="danger">{{ __('Danger') }}</option>
</FormSelect>
<FormActions>
<Button>{{ __('Save') }}</Button>
</FormActions>
</form>
</template>
</SettingsSegment>
</template>
</SettingsLayout>
</PageBody>
</Container>
</Content>
</Page>
</template>
<script>
import TopBar from '../components/TopBar'
import Container from '@/components/Container'
import Content from '@/components/Content'
import Page from '@/components/Page'
import PageHeader from '@/components/PageHeader'
import PageHeaderTitle from '@/components/PageHeaderTitle'
import PageBody from '@/components/PageBody'
import Button from '@/components/Button'
import List from '@/components/List'
import ListItem from '@/components/ListItem'
import StatusBubble from '@/components/StatusBubble'
import NotificationBadge from '@/components/NotificationBadge'
import MainLayout from '@/Layouts/MainLayout'
import SettingsLayout from '@/components/layouts/SettingsLayout'
import SettingsSegment from '@/components/SettingsSegment'
import FormInput from '@/components/forms/FormInput'
import FormTextarea from '@/components/forms/FormTextarea'
import FormSelect from '@/components/forms/FormSelect'
import Form from '@/components/Form'
import FormActions from '@/components/FormActions'
import Tabs from './Tabs'
export default {
metaInfo() {
return {
title: `${this.__('Create alert')}`,
}
},
layout: MainLayout,
components: {
TopBar,
Container,
Content,
Page,
PageHeader,
PageHeaderTitle,
PageBody,
Button,
List,
ListItem,
StatusBubble,
NotificationBadge,
FormInput,
FormSelect,
SettingsLayout,
SettingsSegment,
Form,
FormActions,
FormTextarea,
Tabs,
},
props: {
providers: Object
},
data() {
return {
sending: false,
form: {
type: 'info',
message: null,
expires_at: null,
},
}
},
methods: {
submit() {
this.$inertia.post(this.route('admin.alerts.store'), this.form, {
onStart: () => this.sending = true,
onFinish: () => this.sending = false
});
}
}
}
</script>

View File

@@ -1,145 +0,0 @@
<template>
<Page>
<TopBar/>
<Content>
<Container>
<PageHeader>
<template #start>
<PageHeaderTitle>{{ __('Edit alert') }}</PageHeaderTitle>
</template>
</PageHeader>
<PageBody>
<SettingsLayout>
<template #nav>
<Tabs/>
</template>
<template #segments>
<SettingsSegment>
<template #title>{{ __('Edit') }}</template>
<template #form>
<form class="space-y-4" @submit.prevent="submit">
<FormTextarea :label="__('Message')" :errors="$page.props.errors.message"
v-model="form.message"/>
<FormInput :label="__('Expires at')" placeholder="2020-01-01 00:00:00" :errors="$page.props.errors.expires_at" v-model="form.expires_at"/>
<FormSelect :errors="$page.props.errors.type" :label="__('Type')" v-model="form.type">
<option value="info">{{ __('Informational') }}</option>
<option value="warning">{{ __('Warning') }}</option>
<option value="danger">{{ __('Danger') }}</option>
</FormSelect>
<FormActions>
<Button>{{ __('Save') }}</Button>
<Button variant="danger" type="button" @click="confirmDelete">
{{ __('Delete') }}
</Button>
</FormActions>
</form>
</template>
</SettingsSegment>
</template>
</SettingsLayout>
</PageBody>
</Container>
</Content>
</Page>
</template>
<script>
import TopBar from '../components/TopBar'
import Container from '@/components/Container'
import Content from '@/components/Content'
import Page from '@/components/Page'
import PageHeader from '@/components/PageHeader'
import PageHeaderTitle from '@/components/PageHeaderTitle'
import PageBody from '@/components/PageBody'
import Button from '@/components/Button'
import List from '@/components/List'
import ListItem from '@/components/ListItem'
import StatusBubble from '@/components/StatusBubble'
import NotificationBadge from '@/components/NotificationBadge'
import MainLayout from '@/Layouts/MainLayout'
import SettingsLayout from '@/components/layouts/SettingsLayout'
import SettingsSegment from '@/components/SettingsSegment'
import FormInput from '@/components/forms/FormInput'
import FormTextarea from '@/components/forms/FormTextarea'
import FormSelect from '@/components/forms/FormSelect'
import Form from '@/components/Form'
import FormActions from '@/components/FormActions'
import {useConfirm} from '@/hooks/confirm'
import Tabs from './Tabs'
export default {
metaInfo() {
return {
title: `${this.__('Edit alert')}`,
}
},
layout: MainLayout,
components: {
TopBar,
Container,
Content,
Page,
PageHeader,
PageHeaderTitle,
PageBody,
Button,
List,
ListItem,
StatusBubble,
NotificationBadge,
FormInput,
FormSelect,
SettingsLayout,
SettingsSegment,
Form,
FormTextarea,
FormActions,
Tabs,
},
data() {
return {
sending: false,
form: {
type: this.alert.type,
message: this.alert.message,
expires_at: this.alert.expires_at,
},
}
},
props: {
alert: Object,
},
methods: {
submit() {
this.$inertia.patch(this.route('admin.alerts.update', this.alert.id), this.form, {
onStart: () => this.sending = true,
onFinish: () => this.sending = false
})
},
confirmDelete() {
useConfirm({
title: this.__('Are you sure?'),
message: this.__('Are you sure you want to delete this alert?'),
onConfirm: () => this.delete(),
})
},
delete() {
this.$inertia.delete(this.route('admin.alerts.delete', this.alert.id), {
onStart: () => this.sending = true,
onFinish: () => this.sending = false
})
}
}
}
</script>

View File

@@ -1,120 +0,0 @@
<template>
<Page>
<TopBar/>
<Content>
<Container>
<PageHeader>
<template #start>
<PageHeaderTitle>{{ __('Alerts') }}</PageHeaderTitle>
</template>
</PageHeader>
<PageBody>
<SettingsLayout>
<template #nav>
<Tabs />
</template>
<template #segments>
<SettingsSegment>
<template #title>{{ __('Overview') }}</template>
<template #content>
<Table caption="Alert list overview">
<TableHead>
<TableRow>
<TableHeader>{{ __('Content') }}</TableHeader>
<TableHeader>{{ __('Expires at') }}</TableHeader>
<TableHeader></TableHeader>
</TableRow>
</TableHead>
<TableBody>
<TableRow v-for="alert in alerts.data" :key="alert.id">
<TableData>
<div v-html="alert.message_html"></div>
<p class="text-medium-emphasis">{{ alert.type }}</p>
</TableData>
<TableData>{{ alert.expires_at ? alert.expires_at : '-No expire date-' }}</TableData>
<TableData>
<inertia-link :href="route('admin.alerts.edit', alert.id)"
class="text-primary font-medium">
{{ __('Edit') }}
</inertia-link>
</TableData>
</TableRow>
</TableBody>
</Table>
</template>
</SettingsSegment>
</template>
</SettingsLayout>
</PageBody>
</Container>
</Content>
</Page>
</template>
<script>
import TopBar from '../components/TopBar'
import Container from '@/components/Container'
import Content from '@/components/Content'
import Page from '@/components/Page'
import PageHeader from '@/components/PageHeader'
import PageHeaderTitle from '@/components/PageHeaderTitle'
import PageBody from '@/components/PageBody'
import Button from '@/components/Button'
import List from '@/components/List'
import ListItem from '@/components/ListItem'
import StatusBubble from '@/components/StatusBubble'
import NotificationBadge from '@/components/NotificationBadge'
import MainLayout from '@/Layouts/MainLayout'
import SettingsLayout from '@/components/layouts/SettingsLayout'
import SettingsSegment from '@/components/SettingsSegment'
import Pagination from '@/components/Pagination'
import Tabs from './Tabs';
import Table from '@/components/Table'
import TableHead from '@/components/TableHead'
import TableHeader from '@/components/TableHeader'
import TableRow from '@/components/TableRow'
import TableBody from '@/components/TableBody'
import TableData from '@/components/TableData'
export default {
metaInfo() {
return {
title: `${this.__('Alerts')}`,
}
},
layout: MainLayout,
components: {
TopBar,
Container,
Content,
Page,
PageHeader,
PageHeaderTitle,
PageBody,
Button,
List,
ListItem,
StatusBubble,
NotificationBadge,
SettingsLayout,
SettingsSegment,
Pagination,
Tabs,
Table,
TableHead,
TableHeader,
TableRow,
TableBody,
TableData,
},
props: {
alerts: Object
},
}
</script>

View File

@@ -1,36 +0,0 @@
<template>
<ul class="-ml-4 space-y-1">
<li v-for="item in items">
<inertia-link
class="flex items-center h-10 px-4 font-medium text-medium-emphasis"
:class="{'rounded shadow text-primary bg-surface-3': item.active}"
:href="item.to"
v-html="item.title"
></inertia-link>
</li>
</ul>
</template>
<script>
export default {
data() {
return {
items: [
{
title: this.__('&laquo; Back'),
to: this.route('admin.dashboard'),
},
{
title: this.__('Overview'),
to: this.route('admin.alerts.index'),
active: this.route().current('admin.alerts.index')
},
{
title: this.__('Create'),
to: this.route('admin.alerts.create'),
active: this.route().current('admin.alerts.create')
}
],
}
},
}
</script>

View File

@@ -1,142 +0,0 @@
<template>
<Page>
<TopBar/>
<Content>
<Container>
<PageHeader>
<template #start>
<PageHeaderTitle>{{ __('Application logs') }}</PageHeaderTitle>
</template>
</PageHeader>
<PageBody>
<SettingsLayout>
<template #nav>
<Tabs />
</template>
<template #segments>
<div class="space-y-4">
<FormSelect :label="__('Date')" v-model="searchFilters.date">
<option :value="availableDate" v-for="availableDate in logData.available_dates">{{ availableDate }}</option>
</FormSelect>
<div class="overflow-scroll">
<Table caption="User list overview">
<TableHead>
<TableRow>
<TableHeader>{{ __('Content') }}</TableHeader>
</TableRow>
</TableHead>
<TableBody>
<TableRow v-for="(log, index) in logData.logs" :key="index">
<TableData>
{{ log.message }}
<p class="text-medium-emphasis">{{ log.type }} at {{ log.timestamp }}</p>
</TableData>
</TableRow>
</TableBody>
</Table>
</div>
</div>
</template>
</SettingsLayout>
</PageBody>
</Container>
</Content>
</Page>
</template>
<script>
import TopBar from './components/TopBar.vue'
import Container from '@/components/Container.vue'
import Content from '@/components/Content.vue'
import Page from '@/components/Page.vue'
import PageHeader from '@/components/PageHeader.vue'
import PageHeaderTitle from '@/components/PageHeaderTitle.vue'
import PageBody from '@/components/PageBody.vue'
import Button from '@/components/Button.vue'
import List from '@/components/List.vue'
import ListItem from '@/components/ListItem.vue'
import StatusBubble from '@/components/StatusBubble.vue'
import NotificationBadge from '@/components/NotificationBadge.vue'
import MainLayout from '@/Layouts/MainLayout.vue'
import SettingsLayout from '@/components/layouts/SettingsLayout.vue'
import SettingsSegment from '@/components/SettingsSegment.vue'
import FormInput from '@/components/forms/FormInput.vue'
import FormSelect from '@/components/forms/FormSelect.vue'
import Form from '@/components/Form.vue'
import FormActions from '@/components/FormActions.vue'
import Tabs from './Tabs.vue'
import Table from '@/components/Table.vue'
import TableHead from '@/components/TableHead.vue'
import TableHeader from '@/components/TableHeader.vue'
import TableRow from '@/components/TableRow.vue'
import TableBody from '@/components/TableBody.vue'
import TableData from '@/components/TableData.vue'
import throttle from 'lodash/throttle'
import pickBy from 'lodash/pickBy'
export default {
metaInfo() {
return {
title: `${this.__('Application logs')}`,
}
},
layout: MainLayout,
components: {
TopBar,
Container,
Content,
Page,
PageHeader,
PageHeaderTitle,
PageBody,
Button,
List,
ListItem,
StatusBubble,
NotificationBadge,
FormInput,
FormSelect,
SettingsLayout,
SettingsSegment,
Form,
FormActions,
Tabs,
Table,
TableHead,
TableHeader,
TableRow,
TableBody,
TableData,
},
props: {
logData: Object,
filters: Object
},
data() {
return {
searchFilters: {
date: this.filters.date,
}
}
},
watch: {
searchFilters: {
handler: throttle(function() {
let query = pickBy(this.searchFilters)
this.$inertia.replace(this.route('admin.application-logs', Object.keys(query).length ? query : { remember: 'forget' }))
}, 150),
deep: true,
},
},
}
</script>

View File

@@ -1,148 +0,0 @@
<template>
<Page>
<TopBar/>
<Content>
<Container>
<PageHeader>
<template #start>
<PageHeaderTitle>{{ __('Administration') }}</PageHeaderTitle>
</template>
</PageHeader>
<PageBody>
<SettingsLayout space="space-y-0">
<template #nav>
<Tabs class="mb-16" />
</template>
<template #segments>
<div class="mb-16">
<ul class="grid grid-cols-3 gap-4">
<li class="p-6 rounded shadow bg-surface-3">
<div class="flex space-x-4">
<div>
<IconHarddisk class="w-6 h-6"/>
</div>
<div>
<h3 class="font-semibold text-body" v-text="servers"></h3>
<p class="text-medium-emphasis text-small">{{ __('Servers') }}</p>
</div>
</div>
</li>
<li class="p-6 rounded shadow bg-surface-3">
<div class="flex space-x-4">
<div>
<IconGlobe class="w-6 h-6"/>
</div>
<div>
<h3 class="font-semibold text-body" v-text="sites"></h3>
<p class="text-medium-emphasis text-small">{{ __('Sites') }}</p>
</div>
</div>
</li>
<li class="p-6 rounded shadow bg-surface-3">
<div class="flex space-x-4">
<div>
<IconPerson class="w-6 h-6"/>
</div>
<div>
<h3 class="font-semibold text-body" v-text="users"></h3>
<p class="text-medium-emphasis text-small">{{ __('Users') }}</p>
</div>
</div>
</li>
</ul>
</div>
<div class="pb-4">
<h2>{{ __('Recent logs') }}</h2>
<List>
<ListItem v-for="log in logs.data" :key="log.id">
<template #title>
{{ log.title }}
</template>
<template #subtitle>{{ log.description }}</template>
<template #suffix>{{ log.created_at_human }}</template>
</ListItem>
</List>
</div>
<pagination :links="logs"/>
</template>
</SettingsLayout>
</PageBody>
</Container>
</Content>
</Page>
</template>
<script>
import TopBar from './components/TopBar.vue'
import Container from '@/components/Container.vue'
import Content from '@/components/Content.vue'
import Page from '@/components/Page.vue'
import Pagination from '@/components/Pagination.vue'
import PageHeader from '@/components/PageHeader.vue'
import PageHeaderTitle from '@/components/PageHeaderTitle.vue'
import PageBody from '@/components/PageBody.vue'
import Button from '@/components/Button.vue'
import List from '@/components/List.vue'
import ListItem from '@/components/ListItem.vue'
import StatusBubble from '@/components/StatusBubble.vue'
import NotificationBadge from '@/components/NotificationBadge.vue'
import MainLayout from '@/Layouts/MainLayout.vue'
import SettingsLayout from '@/components/layouts/SettingsLayout.vue'
import SettingsSegment from '@/components/SettingsSegment.vue'
import FormInput from '@/components/forms/FormInput.vue'
import Form from '@/components/Form.vue'
import FormActions from '@/components/FormActions.vue'
import Tabs from './Tabs.vue'
import IconPerson from '@/components/icons/IconPerson.vue'
import IconGlobe from '@/components/icons/IconGlobe.vue'
import IconStorage from '@/components/icons/IconStorage.vue'
import IconHarddisk from '@/components/icons/IconHarddisk.vue'
export default {
metaInfo() {
return {
title: `${this.__('Administration')}`,
}
},
layout: MainLayout,
components: {
TopBar,
Container,
Content,
Page,
Pagination,
PageHeader,
PageHeaderTitle,
PageBody,
Button,
List,
ListItem,
StatusBubble,
NotificationBadge,
FormInput,
SettingsLayout,
SettingsSegment,
Form,
FormActions,
Tabs,
IconPerson,
IconGlobe,
IconStorage,
IconHarddisk,
},
props: {
servers: Number,
sites: Number,
users: Number,
logs: [Array, Object],
}
}
</script>

View File

@@ -1,125 +0,0 @@
<template>
<Page>
<TopBar/>
<Content>
<Container>
<PageHeader>
<template #start>
<PageHeaderTitle>{{ __('Create article') }}</PageHeaderTitle>
</template>
</PageHeader>
<PageBody>
<SettingsLayout>
<template #nav>
<Tabs/>
</template>
<template #segments>
<SettingsSegment>
<template #title>{{ __('Create') }}</template>
<template #form>
<form class="space-y-4" @submit.prevent="submit">
<FormInput :label="__('Title')" :errors="$page.props.errors.title"
v-model="form.title"/>
<FormSelect :label="__('Category')" :errors="$page.props.errors.category_id" v-model="form.category_id">
<option v-for="(title, id) in categories" :value="id" v-text="title"></option>
</FormSelect>
<FormTextarea rows="10" :helper-text="__('You may use markdown in this field for markup')" :label="__('Content')"
:errors="$page.props.errors.content" v-model="form.content"/>
<FormActions>
<Button>{{ __('Save') }}</Button>
</FormActions>
</form>
</template>
</SettingsSegment>
</template>
</SettingsLayout>
</PageBody>
</Container>
</Content>
</Page>
</template>
<script>
import TopBar from '../../components/TopBar'
import Container from '@/components/Container'
import Content from '@/components/Content'
import Page from '@/components/Page'
import PageHeader from '@/components/PageHeader'
import PageHeaderTitle from '@/components/PageHeaderTitle'
import PageBody from '@/components/PageBody'
import Button from '@/components/Button'
import List from '@/components/List'
import ListItem from '@/components/ListItem'
import StatusBubble from '@/components/StatusBubble'
import NotificationBadge from '@/components/NotificationBadge'
import MainLayout from '@/Layouts/MainLayout'
import SettingsLayout from '@/components/layouts/SettingsLayout'
import SettingsSegment from '@/components/SettingsSegment'
import FormInput from '@/components/forms/FormInput'
import FormSelect from '@/components/forms/FormSelect'
import FormTextarea from '@/components/forms/FormTextarea'
import Form from '@/components/Form'
import FormActions from '@/components/FormActions'
import Tabs from '../Tabs'
export default {
metaInfo() {
return {
title: `${this.__('Create article')}`,
}
},
layout: MainLayout,
components: {
TopBar,
Container,
Content,
Page,
PageHeader,
PageHeaderTitle,
PageBody,
Button,
List,
ListItem,
StatusBubble,
NotificationBadge,
FormInput,
FormTextarea,
SettingsLayout,
SettingsSegment,
Form,
FormActions,
Tabs,
FormSelect,
},
props: {
categories: Object
},
data() {
return {
sending: false,
form: {
title: null,
description: null,
category_id: null,
},
}
},
methods: {
submit() {
this.$inertia.post(this.route('admin.documentation.articles.store'), this.form, {
onStart: () => this.sending = true,
onFinish: () => this.sending = false,
});
}
}
}
</script>

View File

@@ -1,126 +0,0 @@
<template>
<Page>
<TopBar/>
<Content>
<Container>
<PageHeader>
<template #start>
<PageHeaderTitle>{{ __('Edit article') }}</PageHeaderTitle>
</template>
</PageHeader>
<PageBody>
<SettingsLayout>
<template #nav>
<Tabs/>
</template>
<template #segments>
<SettingsSegment>
<template #title>{{ __('Edit') }}</template>
<template #form>
<form class="space-y-4" @submit.prevent="submit">
<FormInput :label="__('Title')" :errors="$page.props.errors.title"
v-model="form.title"/>
<FormSelect :label="__('Category')" :errors="$page.props.errors.category_id" v-model="form.category_id">
<option v-for="(title, id) in categories" :value="id" v-text="title"></option>
</FormSelect>
<FormTextarea rows="10" helper-text="You may use markdown in this field for markup" :label="__('Content')" :errors="$page.props.errors.content"
v-model="form.content"/>
<FormActions>
<Button>{{ __('Save') }}</Button>
</FormActions>
</form>
</template>
</SettingsSegment>
</template>
</SettingsLayout>
</PageBody>
</Container>
</Content>
</Page>
</template>
<script>
import TopBar from '../../components/TopBar'
import Container from '@/components/Container'
import Content from '@/components/Content'
import Page from '@/components/Page'
import PageHeader from '@/components/PageHeader'
import PageHeaderTitle from '@/components/PageHeaderTitle'
import PageBody from '@/components/PageBody'
import Button from '@/components/Button'
import List from '@/components/List'
import ListItem from '@/components/ListItem'
import StatusBubble from '@/components/StatusBubble'
import NotificationBadge from '@/components/NotificationBadge'
import MainLayout from '@/Layouts/MainLayout'
import SettingsLayout from '@/components/layouts/SettingsLayout'
import SettingsSegment from '@/components/SettingsSegment'
import FormInput from '@/components/forms/FormInput'
import FormSelect from '@/components/forms/FormSelect'
import FormTextarea from '@/components/forms/FormTextarea'
import Form from '@/components/Form'
import FormActions from '@/components/FormActions'
import Tabs from '../Tabs'
export default {
metaInfo() {
return {
title: `${this.__('Edit article')}`,
}
},
layout: MainLayout,
components: {
TopBar,
Container,
Content,
Page,
PageHeader,
PageHeaderTitle,
PageBody,
Button,
List,
ListItem,
StatusBubble,
NotificationBadge,
FormInput,
FormTextarea,
SettingsLayout,
SettingsSegment,
Form,
FormSelect,
FormActions,
Tabs,
},
props: {
article: Object,
categories: Object,
},
data() {
return {
sending: false,
form: {
title: this.article.title,
content: this.article.content,
category_id: this.article.documentation_category_id,
},
}
},
methods: {
submit() {
this.$inertia.patch(this.route('admin.documentation.articles.update', this.article.id), this.form, {
onStart: () => this.sending = true,
onFinish: () => this.sending = false,
});
}
}
}
</script>

View File

@@ -1,133 +0,0 @@
<template>
<Page>
<TopBar/>
<Content>
<Container>
<PageHeader>
<template #start>
<PageHeaderTitle>{{ __('Articles') }}</PageHeaderTitle>
</template>
</PageHeader>
<PageBody>
<SettingsLayout>
<template #nav>
<Tabs />
</template>
<template #segments>
<SettingsSegment>
<template #title>{{ __('Overview') }}</template>
<template #content>
<Table caption="Documentation category list overview">
<TableHead>
<TableRow>
<TableHeader>{{ __('Title') }}</TableHeader>
<TableHeader>{{ __('Category') }}</TableHeader>
<TableHeader></TableHeader>
</TableRow>
</TableHead>
<TableBody>
<TableRow v-for="article in articles.data" :key="article.id">
<TableData>
{{ article.title }}
</TableData>
<TableData>{{ article.category.title }}</TableData>
<TableData>
<inertia-link :href="route('admin.documentation.articles.edit', article.id)"
class="text-primary font-medium">
{{ __('Edit') }}
</inertia-link>
</TableData>
</TableRow>
</TableBody>
</Table>
</template>
</SettingsSegment>
</template>
</SettingsLayout>
</PageBody>
</Container>
</Content>
</Page>
</template>
<script>
import TopBar from '../../components/TopBar'
import Container from '@/components/Container'
import Content from '@/components/Content'
import Page from '@/components/Page'
import PageHeader from '@/components/PageHeader'
import PageHeaderTitle from '@/components/PageHeaderTitle'
import PageBody from '@/components/PageBody'
import Button from '@/components/Button'
import List from '@/components/List'
import ListItem from '@/components/ListItem'
import StatusBubble from '@/components/StatusBubble'
import NotificationBadge from '@/components/NotificationBadge'
import MainLayout from '@/Layouts/MainLayout'
import SettingsLayout from '@/components/layouts/SettingsLayout'
import SettingsSegment from '@/components/SettingsSegment'
import Pagination from '@/components/Pagination'
import {useNotification} from '@/hooks/notification'
import Tabs from '../Tabs';
import Table from '@/components/Table'
import TableHead from '@/components/TableHead'
import TableHeader from '@/components/TableHeader'
import TableRow from '@/components/TableRow'
import TableBody from '@/components/TableBody'
import TableData from '@/components/TableData'
export default {
metaInfo() {
return {
title: `${this.__('Articles')}`,
}
},
layout: MainLayout,
components: {
TopBar,
Container,
Content,
Page,
PageHeader,
PageHeaderTitle,
PageBody,
Button,
List,
ListItem,
StatusBubble,
NotificationBadge,
SettingsLayout,
SettingsSegment,
Pagination,
Tabs,
Table,
TableHead,
TableHeader,
TableRow,
TableBody,
TableData,
},
props: {
articles: Object
},
data() {
return {
}
},
mounted() {
},
methods: {
useNotification,
},
}
</script>

View File

@@ -1,114 +0,0 @@
<template>
<Page>
<TopBar/>
<Content>
<Container>
<PageHeader>
<template #start>
<PageHeaderTitle>{{ __('Create category') }}</PageHeaderTitle>
</template>
</PageHeader>
<PageBody>
<SettingsLayout>
<template #nav>
<Tabs/>
</template>
<template #segments>
<SettingsSegment>
<template #title>{{ __('Create') }}</template>
<template #form>
<form class="space-y-4" @submit.prevent="submit">
<FormInput :label="__('Title')" :errors="$page.props.errors.title"
v-model="form.title"/>
<FormTextarea :label="__('Description')" :helper-text="__('You may use markdown in this field for markup')" :errors="$page.props.errors.description" v-model="form.description" />
<FormActions>
<Button>{{ __('Save') }}</Button>
</FormActions>
</form>
</template>
</SettingsSegment>
</template>
</SettingsLayout>
</PageBody>
</Container>
</Content>
</Page>
</template>
<script>
import TopBar from '../components/TopBar'
import Container from '@/components/Container'
import Content from '@/components/Content'
import Page from '@/components/Page'
import PageHeader from '@/components/PageHeader'
import PageHeaderTitle from '@/components/PageHeaderTitle'
import PageBody from '@/components/PageBody'
import Button from '@/components/Button'
import List from '@/components/List'
import ListItem from '@/components/ListItem'
import StatusBubble from '@/components/StatusBubble'
import NotificationBadge from '@/components/NotificationBadge'
import MainLayout from '@/Layouts/MainLayout'
import SettingsLayout from '@/components/layouts/SettingsLayout'
import SettingsSegment from '@/components/SettingsSegment'
import FormInput from '@/components/forms/FormInput'
import FormTextarea from '@/components/forms/FormTextarea'
import Form from '@/components/Form'
import FormActions from '@/components/FormActions'
import Tabs from './Tabs'
export default {
metaInfo() {
return {
title: `${this.__('Create category')}`,
}
},
layout: MainLayout,
components: {
TopBar,
Container,
Content,
Page,
PageHeader,
PageHeaderTitle,
PageBody,
Button,
List,
ListItem,
StatusBubble,
NotificationBadge,
FormInput,
FormTextarea,
SettingsLayout,
SettingsSegment,
Form,
FormActions,
Tabs,
},
data() {
return {
sending: false,
form: {
title: null,
description: null,
},
}
},
methods: {
submit() {
this.$inertia.post(this.route('admin.documentation.store'), this.form, {
onStart: () => this.sending = true,
onFinish: () => this.sending = false,
});
}
}
}
</script>

View File

@@ -1,118 +0,0 @@
<template>
<Page>
<TopBar/>
<Content>
<Container>
<PageHeader>
<template #start>
<PageHeaderTitle>{{ __('Edit category') }}</PageHeaderTitle>
</template>
</PageHeader>
<PageBody>
<SettingsLayout>
<template #nav>
<Tabs/>
</template>
<template #segments>
<SettingsSegment>
<template #title>{{ __('Edit') }}</template>
<template #form>
<form class="space-y-4" @submit.prevent="submit">
<FormInput :label="__('Title')" :errors="$page.props.errors.title"
v-model="form.title"/>
<FormTextarea :label="__('Description')" helper-text="You may use markdown in this field for markup" :errors="$page.props.errors.description" v-model="form.description" />
<FormActions>
<Button>{{ __('Save') }}</Button>
</FormActions>
</form>
</template>
</SettingsSegment>
</template>
</SettingsLayout>
</PageBody>
</Container>
</Content>
</Page>
</template>
<script>
import TopBar from '../components/TopBar'
import Container from '@/components/Container'
import Content from '@/components/Content'
import Page from '@/components/Page'
import PageHeader from '@/components/PageHeader'
import PageHeaderTitle from '@/components/PageHeaderTitle'
import PageBody from '@/components/PageBody'
import Button from '@/components/Button'
import List from '@/components/List'
import ListItem from '@/components/ListItem'
import StatusBubble from '@/components/StatusBubble'
import NotificationBadge from '@/components/NotificationBadge'
import MainLayout from '@/Layouts/MainLayout'
import SettingsLayout from '@/components/layouts/SettingsLayout'
import SettingsSegment from '@/components/SettingsSegment'
import FormInput from '@/components/forms/FormInput'
import FormTextarea from '@/components/forms/FormTextarea'
import Form from '@/components/Form'
import FormActions from '@/components/FormActions'
import Tabs from './Tabs'
export default {
metaInfo() {
return {
title: `${this.__('Create category')}`,
}
},
layout: MainLayout,
components: {
TopBar,
Container,
Content,
Page,
PageHeader,
PageHeaderTitle,
PageBody,
Button,
List,
ListItem,
StatusBubble,
NotificationBadge,
FormInput,
FormTextarea,
SettingsLayout,
SettingsSegment,
Form,
FormActions,
Tabs,
},
props: {
category: Object
},
data() {
return {
sending: false,
form: {
title: this.category.title,
description: this.category.description,
},
}
},
methods: {
submit() {
this.$inertia.patch(this.route('admin.documentation.update', this.category.id), this.form, {
onStart: () => this.sending = true,
onFinish: () => this.sending = false,
});
}
}
}
</script>

View File

@@ -1,131 +0,0 @@
<template>
<Page>
<TopBar/>
<Content>
<Container>
<PageHeader>
<template #start>
<PageHeaderTitle>{{ __('Documentation') }}</PageHeaderTitle>
</template>
</PageHeader>
<PageBody>
<SettingsLayout>
<template #nav>
<Tabs />
</template>
<template #segments>
<SettingsSegment>
<template #title>{{ __('Overview') }}</template>
<template #content>
<Table caption="Documentation category list overview">
<TableHead>
<TableRow>
<TableHeader>{{ __('Title') }}</TableHeader>
<TableHeader></TableHeader>
</TableRow>
</TableHead>
<TableBody>
<TableRow v-for="category in categories.data" :key="category.id">
<TableData>
{{ category.title }}
</TableData>
<TableData>
<inertia-link :href="route('admin.documentation.edit', category.id)"
class="text-primary font-medium">
{{ __('Edit') }}
</inertia-link>
</TableData>
</TableRow>
</TableBody>
</Table>
</template>
</SettingsSegment>
</template>
</SettingsLayout>
</PageBody>
</Container>
</Content>
</Page>
</template>
<script>
import TopBar from '../components/TopBar'
import Container from '@/components/Container'
import Content from '@/components/Content'
import Page from '@/components/Page'
import PageHeader from '@/components/PageHeader'
import PageHeaderTitle from '@/components/PageHeaderTitle'
import PageBody from '@/components/PageBody'
import Button from '@/components/Button'
import List from '@/components/List'
import ListItem from '@/components/ListItem'
import StatusBubble from '@/components/StatusBubble'
import NotificationBadge from '@/components/NotificationBadge'
import MainLayout from '@/Layouts/MainLayout'
import SettingsLayout from '@/components/layouts/SettingsLayout'
import SettingsSegment from '@/components/SettingsSegment'
import Pagination from '@/components/Pagination'
import {useNotification} from '@/hooks/notification'
import Tabs from './Tabs';
import Table from '@/components/Table'
import TableHead from '@/components/TableHead'
import TableHeader from '@/components/TableHeader'
import TableRow from '@/components/TableRow'
import TableBody from '@/components/TableBody'
import TableData from '@/components/TableData'
export default {
metaInfo() {
return {
title: `${this.__('Documentation')}`,
}
},
layout: MainLayout,
components: {
TopBar,
Container,
Content,
Page,
PageHeader,
PageHeaderTitle,
PageBody,
Button,
List,
ListItem,
StatusBubble,
NotificationBadge,
SettingsLayout,
SettingsSegment,
Pagination,
Tabs,
Table,
TableHead,
TableHeader,
TableRow,
TableBody,
TableData,
},
props: {
categories: Object
},
data() {
return {
}
},
mounted() {
},
methods: {
useNotification,
},
}
</script>

View File

@@ -1,43 +0,0 @@
<template>
<ul class="-ml-4 space-y-1">
<li v-for="item in items">
<inertia-link
class="flex items-center h-10 px-4 font-medium text-medium-emphasis"
:class="{'rounded shadow text-primary bg-surface-3': item.active}"
:href="item.to"
>{{ item.title }} {{ item.route }}
</inertia-link
>
</li>
</ul>
</template>
<script>
export default {
data() {
return {
items: [
{
title: this.__('Categories'),
to: this.route('admin.documentation.index'),
active: this.route().current('admin.documentation.index')
},
{
title: this.__('Create category'),
to: this.route('admin.documentation.create'),
active: this.route().current('admin.documentation.create')
},
{
title: this.__('Articles'),
to: this.route('admin.documentation.articles.index'),
active: this.route().current('admin.documentation.articles.index')
},
{
title: this.__('Create article'),
to: this.route('admin.documentation.articles.create'),
active: this.route().current('admin.documentation.articles.create')
},
],
}
},
}
</script>

View File

@@ -1,252 +0,0 @@
<template>
<Page>
<TopBar/>
<Content>
<Container>
<PageHeader>
<template #start>
<PageHeaderTitle>{{ __('Create package') }}</PageHeaderTitle>
</template>
</PageHeader>
<PageBody>
<SettingsLayout>
<template #nav>
<Tabs/>
</template>
<template #segments>
<SettingsSegment>
<template #title>{{ __('Create') }}</template>
<template #subtitle>
{{
__('Create a new package here to attach to your users. You can create as many packages as you want, the advantage is that you are able to provide custom packages for your users.')
}}
</template>
<template #form>
<form class="space-y-4" @submit.prevent="submit">
<FormInput :label="__('Name')" :errors="$page.props.errors.name"
v-model="form.name"/>
<FormInput :label="__('Maximum sites')" type="number" min="0"
:errors="$page.props.errors.maximum_sites"
helper-text="Set to 0 for unlimited"
v-model="form.maximum_sites"/>
<FormInput :label="__('Maximum servers')" type="number" min="0"
:errors="$page.props.errors.maximum_servers"
helper-text="Set to 0 for unlimited"
v-model="form.maximum_servers"/>
<FormInput :label="__('Plan ID')" :errors="$page.props.errors.plan_id"
helper-text="Enter the pricing ID from Stripe here"
v-model="form.plan_id"/>
<FormInput v-if="form.plan_id" :label="__('Monthly price')"
helper-text="Fill this in if you want it to be monthly payments"
:errors="$page.props.errors.price_monthly"
v-model="form.price_monthly"/>
<FormInput v-if="form.plan_id" :label="__('Yearly price')"
helper-text="Fill this in if you want it to be yearly payments"
:errors="$page.props.errors.price_yearly"
v-model="form.price_yearly"/>
<FormSelect :errors="$page.props.errors.currency" v-if="form.plan_id"
:label="__('Currency')" v-model="form.currency">
<option value="usd">{{ __('USD $') }}</option>
<option value="eur">{{ __('Euro €') }}</option>
<option value="gbp">{{ __('GBP £') }}</option>
<option value="nok">{{ __('NOK (Norwegian Krone)') }}</option>
<option value="aud">{{ __('AUD (Australian dollar)') }}</option>
<option value="cad">{{ __('CAD (Canadian dollar)') }}</option>
<option value="inr">{{ __('INR ₹ (Indian rupee)') }}</option>
<option value="thb">{{ __('THB (Thai bath)') }}</option>
<option value="brl">{{ __('BRL R$ (Brazilian Real)') }}</option>
</FormSelect>
<div class="space-y-4">
<h3 class="text-base leading-6 font-medium border-b border-dotted border-medium-emphasis pb-1">
{{ __('Server permissions') }}</h3>
<div>
<input id="server_create" class="form-checkbox" type="checkbox"
v-model="form.server_permissions['create']">
<label for="server_create"
class="ml-2 text-sm">{{ __('Allow server creation') }}</label>
<p class="text-small mt-1 text-medium-emphasis">
{{ __('This will allow users to create servers') }}
</p>
</div>
<div>
<input id="server_update" class="form-checkbox" type="checkbox"
v-model="form.server_permissions['update']">
<label for="server_update"
class="ml-2 text-sm">{{ __('Allow server updating') }}</label>
<p class="text-small mt-1 text-medium-emphasis">
{{ __('This will allow users to update servers') }}
</p>
</div>
<div>
<input id="server_delete" class="form-checkbox" type="checkbox"
v-model="form.server_permissions['delete']">
<label for="server_delete"
class="ml-2 text-sm">{{ __('Allow server deletion') }}</label>
<p class="text-small mt-1 text-medium-emphasis">
{{ __('This will allow users to delete servers') }}
</p>
</div>
</div>
<div class="space-y-2">
<h3 class="text-base leading-6 font-medium border-b border-dotted border-medium-emphasis pb-1">
{{ __('Site permissions') }}</h3>
<div>
<input id="site_create" class="form-checkbox" type="checkbox"
v-model="form.site_permissions['create']">
<label for="site_create"
class="ml-2 text-sm">{{ __('Allow site creation') }}</label>
<p class="text-small mt-1 text-medium-emphasis">
{{ __('This will allow users to create sites') }}
</p>
</div>
<div>
<input id="site_update" class="form-checkbox" type="checkbox"
v-model="form.site_permissions['update']">
<label for="site_update"
class="ml-2 text-sm">{{ __('Allow site updating') }}</label>
<p class="text-small mt-1 text-medium-emphasis">
{{ __('This will allow users to update sites') }}
</p>
</div>
<div>
<input id="site_delete" class="form-checkbox" type="checkbox"
v-model="form.site_permissions['delete']">
<label for="site_delete"
class="ml-2 text-sm">{{ __('Allow site deletion') }}</label>
<p class="text-small mt-1 text-medium-emphasis">
{{ __('This will allow users to delete sites') }}
</p>
</div>
</div>
<div class="space-y-2" v-if="form.server_permissions['create']">
<h3 class="text-base leading-6 font-medium border-b border-dotted border-medium-emphasis pb-1">
{{ __('Available server providers') }}</h3>
<div v-if="!Object.keys(providers).length" class="bg-primary text-on-primary px-4 py-3 rounded relative space-y-2" role="alert">
<p class="block">
There are no server providers to select from. You can synchronize new server providers from the Services tab.
</p>
</div>
<div v-for="(name, id) in providers">
<input :id="`provider-${id}`" :value="id" v-model="form.providers"
class="form-checkbox" type="checkbox">
<label :for="`provider-${id}`" class="ml-2 text-sm">{{ name }}</label>
</div>
</div>
<FormActions>
<Button>{{ __('Save') }}</Button>
</FormActions>
</form>
</template>
</SettingsSegment>
</template>
</SettingsLayout>
</PageBody>
</Container>
</Content>
</Page>
</template>
<script>
import TopBar from '../components/TopBar'
import Container from '@/components/Container'
import Content from '@/components/Content'
import Page from '@/components/Page'
import PageHeader from '@/components/PageHeader'
import PageHeaderTitle from '@/components/PageHeaderTitle'
import PageBody from '@/components/PageBody'
import Button from '@/components/Button'
import List from '@/components/List'
import ListItem from '@/components/ListItem'
import StatusBubble from '@/components/StatusBubble'
import NotificationBadge from '@/components/NotificationBadge'
import MainLayout from '@/Layouts/MainLayout'
import SettingsLayout from '@/components/layouts/SettingsLayout'
import SettingsSegment from '@/components/SettingsSegment'
import FormInput from '@/components/forms/FormInput'
import FormSelect from '@/components/forms/FormSelect'
import Form from '@/components/Form'
import FormActions from '@/components/FormActions'
import Tabs from './Tabs'
export default {
metaInfo() {
return {
title: `${this.__('Create package')}`,
}
},
layout: MainLayout,
components: {
TopBar,
Container,
Content,
Page,
PageHeader,
PageHeaderTitle,
PageBody,
Button,
List,
ListItem,
StatusBubble,
NotificationBadge,
FormInput,
FormSelect,
SettingsLayout,
SettingsSegment,
Form,
FormActions,
Tabs,
},
props: {
providers: [Array, Object]
},
data() {
return {
sending: false,
form: {
name: null,
plan_id: null,
currency: 'usd',
maximum_sites: 10,
maximum_servers: 1,
server_permissions: {
create: false,
update: false,
delete: false,
},
site_permissions: {
create: false,
update: false,
delete: false
},
price_monthly: null,
price_yearly: null,
providers: []
},
}
},
methods: {
submit() {
this.$inertia.post(this.route('admin.packages.store'), this.form, {
onStart: () => this.sending = true,
onFinish: () => this.sending = false
})
}
}
}
</script>

View File

@@ -1,255 +0,0 @@
<template>
<Page>
<TopBar/>
<Content>
<Container>
<PageHeader>
<template #start>
<PageHeaderTitle>{{ __('Edit package') }}</PageHeaderTitle>
</template>
</PageHeader>
<PageBody>
<SettingsLayout>
<template #nav>
<Tabs/>
</template>
<template #segments>
<SettingsSegment>
<template #title>{{ __('Edit') }}</template>
<template #form>
<form class="space-y-4" @submit.prevent="submit">
<FormInput :label="__('Name')" :errors="$page.props.errors.name" v-model="form.name"/>
<FormInput :label="__('Maximum sites')" type="number" min="0"
helper-text="Set to 0 for unlimited"
:errors="$page.props.errors.maximum_sites" v-model="form.maximum_sites"/>
<FormInput :label="__('Maximum servers')" type="number" min="0"
:errors="$page.props.errors.maximum_servers"
helper-text="Set to 0 for unlimited"
v-model="form.maximum_servers"/>
<FormInput :label="__('Plan ID')" :errors="$page.props.errors.plan_id"
helper-text="Enter the pricing ID from Stripe here"
v-model="form.plan_id"/>
<FormInput v-if="form.plan_id" :label="__('Monthly price')"
helper-text="Fill this in if you want it to be monthly payments"
:errors="$page.props.errors.price_monthly" v-model="form.price_monthly"/>
<FormInput v-if="form.plan_id" :label="__('Yearly price')"
helper-text="Fill this in if you want it to be yearly payments"
:errors="$page.props.errors.price_yearly" v-model="form.price_yearly"/>
<FormSelect :errors="$page.props.errors.currency" v-if="form.plan_id" :label="__('Currency')" v-model="form.currency">
<option value="usd">{{ __('USD $') }}</option>
<option value="eur">{{ __('Euro €') }}</option>
<option value="gbp">{{ __('GBP £') }}</option>
<option value="nok">{{ __('NOK (Norwegian Krone)') }}</option>
<option value="aud">{{ __('AUD (Australian dollar)') }}</option>
<option value="cad">{{ __('CAD (Canadian dollar)') }}</option>
<option value="inr">{{ __('INR ₹ (Indian rupee)') }}</option>
<option value="thb">{{ __('THB (Thai bath)') }}</option>
<option value="brl">{{ __('BRL R$ (Brazilian Real)') }}</option>
</FormSelect>
<div class="space-y-4">
<h3 class="text-base leading-6 font-medium border-b border-dotted border-medium-emphasis pb-1">
{{ __('Server permissions') }}</h3>
<div>
<input id="server_create" class="form-checkbox" type="checkbox"
v-model="form.server_permissions['create']">
<label for="server_create"
class="ml-2 text-sm">{{ __('Allow server creation') }}</label>
<p class="text-small mt-1 text-medium-emphasis">
{{ __('This will allow users to create servers') }}
</p>
</div>
<div>
<input id="server_update" class="form-checkbox" type="checkbox"
v-model="form.server_permissions['update']">
<label for="server_update"
class="ml-2 text-sm">{{ __('Allow server updating') }}</label>
<p class="text-small mt-1 text-medium-emphasis">
{{ __('This will allow users to update servers') }}
</p>
</div>
<div>
<input id="server_delete" class="form-checkbox" type="checkbox"
v-model="form.server_permissions['delete']">
<label for="server_delete"
class="ml-2 text-sm">{{ __('Allow server deletion') }}</label>
<p class="text-small mt-1 text-medium-emphasis">
{{ __('This will allow users to delete servers') }}
</p>
</div>
</div>
<div class="space-y-2">
<h3 class="text-base leading-6 font-medium border-b border-dotted border-medium-emphasis pb-1">
{{ __('Site permissions') }}</h3>
<div>
<input id="site_create" class="form-checkbox" type="checkbox"
v-model="form.site_permissions['create']">
<label for="site_create"
class="ml-2 text-sm">{{ __('Allow site creation') }}</label>
<p class="text-small mt-1 text-medium-emphasis">
{{ __('This will allow users to create sites') }}
</p>
</div>
<div>
<input id="site_update" class="form-checkbox" type="checkbox"
v-model="form.site_permissions['update']">
<label for="site_update"
class="ml-2 text-sm">{{ __('Allow site updating') }}</label>
<p class="text-small mt-1 text-medium-emphasis">
{{ __('This will allow users to update sites') }}
</p>
</div>
<div>
<input id="site_delete" class="form-checkbox" type="checkbox"
v-model="form.site_permissions['delete']">
<label for="site_delete"
class="ml-2 text-sm">{{ __('Allow site deletion') }}</label>
<p class="text-small mt-1 text-medium-emphasis">
{{ __('This will allow users to delete sites') }}
</p>
</div>
</div>
<div class="space-y-2" v-if="form.server_permissions['create']">
<h3 class="text-base leading-6 font-medium border-b border-dotted border-medium-emphasis pb-1">
{{ __('Available server providers') }}</h3>
<div v-if="!Object.keys(providers).length" class="bg-primary text-on-primary px-4 py-3 rounded relative space-y-2" role="alert">
<p class="block">
There are no server providers to select from. You can synchronize new server providers from the Services tab.
</p>
</div>
<div v-for="(name, id) in providers">
<input :id="`provider-${id}`" :value="id" v-model="form.providers"
class="form-checkbox" type="checkbox">
<label :for="`provider-${id}`" class="ml-2 text-sm">{{ name }}</label>
</div>
</div>
<FormActions>
<Button>{{ __('Save') }}</Button>
<Button variant="danger" type="button" @click="confirmDelete">
{{ __('Delete') }}
</Button>
</FormActions>
</form>
</template>
</SettingsSegment>
</template>
</SettingsLayout>
</PageBody>
</Container>
</Content>
</Page>
</template>
<script>
import TopBar from '../components/TopBar'
import Container from '@/components/Container'
import Content from '@/components/Content'
import Page from '@/components/Page'
import PageHeader from '@/components/PageHeader'
import PageHeaderTitle from '@/components/PageHeaderTitle'
import PageBody from '@/components/PageBody'
import Button from '@/components/Button'
import List from '@/components/List'
import ListItem from '@/components/ListItem'
import StatusBubble from '@/components/StatusBubble'
import NotificationBadge from '@/components/NotificationBadge'
import MainLayout from '@/Layouts/MainLayout'
import SettingsLayout from '@/components/layouts/SettingsLayout'
import SettingsSegment from '@/components/SettingsSegment'
import FormInput from '@/components/forms/FormInput'
import FormSelect from '@/components/forms/FormSelect'
import Form from '@/components/Form'
import FormActions from '@/components/FormActions'
import {useConfirm} from '@/hooks/confirm'
import Tabs from './Tabs'
export default {
metaInfo() {
return {
title: `${this.__('Edit package')}`,
}
},
layout: MainLayout,
components: {
TopBar,
Container,
Content,
Page,
PageHeader,
PageHeaderTitle,
PageBody,
Button,
List,
ListItem,
StatusBubble,
NotificationBadge,
FormInput,
FormSelect,
SettingsLayout,
SettingsSegment,
Form,
FormActions,
Tabs,
},
data() {
return {
sending: false,
form: {
name: this.package.name,
currency: this.package.currency,
plan_id: this.package.plan_id,
maximum_sites: this.package.maximum_sites,
maximum_servers: this.package.maximum_servers,
server_permissions: this.package.server_permissions ?? [],
site_permissions: this.package.site_permissions ?? [],
price_monthly: this.package.price_monthly,
price_yearly: this.package.price_yearly,
providers: this.syncedProviders ?? []
},
}
},
props: {
package: Object,
providers: [Array, Object],
syncedProviders: Array,
},
methods: {
submit() {
this.$inertia.patch(this.route('admin.packages.update', this.package.id), this.form, {
onStart: () => this.sending = true,
onFinish: () => this.sending = false
})
},
confirmDelete() {
useConfirm({
title: this.__('Are you sure?'),
message: this.__('Are you sure you want to delete this package? Everything associated with this package will be detached.'),
onConfirm: () => this.delete(),
})
},
delete() {
this.$inertia.delete(this.route('admin.packages.destroy', this.package.id), {
onStart: () => this.sending = true,
onFinish: () => this.sending = false
})
}
}
}
</script>

View File

@@ -1,139 +0,0 @@
<template>
<Page>
<TopBar/>
<Content>
<Container>
<PageHeader>
<template #start>
<PageHeaderTitle>{{ __('Packages') }}</PageHeaderTitle>
</template>
</PageHeader>
<PageBody>
<SettingsLayout>
<template #nav>
<Tabs />
</template>
<template #segments>
<SettingsSegment>
<template #title>{{ __('Overview') }}</template>
<template #content>
<Table caption="Package list overview">
<TableHead>
<TableRow>
<TableHeader>{{ __('Name') }}</TableHeader>
<TableHeader>Max. sites</TableHeader>
<TableHeader>Max. servers</TableHeader>
<TableHeader>{{ __('Users') }}</TableHeader>
<TableHeader></TableHeader>
</TableRow>
</TableHead>
<TableBody>
<TableRow v-for="webPackage in packages" :key="webPackage.id">
<TableData>
{{ webPackage.name }}
<p class="text-medium-emphasis" v-if="webPackage.plan_id">Attached to stripe &centerdot; {{ webPackage.price_monthly }} {{ webPackage.currency }}</p>
</TableData>
<TableData>{{ webPackage.maximum_sites === 0 ? 'Unlimited' : webPackage.maximum_sites }}</TableData>
<TableData>{{ webPackage.maximum_servers === 0 ? 'Unlimited' : webPackage.maximum_servers }}</TableData>
<TableData>{{ webPackage.users_count }}</TableData>
<TableData>
<inertia-link :href="route('admin.packages.edit', webPackage.id)"
class="text-primary font-medium">
{{ __('Edit') }}
</inertia-link>
</TableData>
</TableRow>
</TableBody>
</Table>
</template>
</SettingsSegment>
</template>
</SettingsLayout>
</PageBody>
</Container>
</Content>
</Page>
</template>
<script>
import TopBar from '../components/TopBar'
import Container from '@/components/Container'
import Content from '@/components/Content'
import Page from '@/components/Page'
import PageHeader from '@/components/PageHeader'
import PageHeaderTitle from '@/components/PageHeaderTitle'
import PageBody from '@/components/PageBody'
import Button from '@/components/Button'
import List from '@/components/List'
import ListItem from '@/components/ListItem'
import StatusBubble from '@/components/StatusBubble'
import NotificationBadge from '@/components/NotificationBadge'
import MainLayout from '@/Layouts/MainLayout'
import SettingsLayout from '@/components/layouts/SettingsLayout'
import SettingsSegment from '@/components/SettingsSegment'
import Pagination from '@/components/Pagination'
import Tabs from './Tabs';
import Table from '@/components/Table'
import TableHead from '@/components/TableHead'
import TableHeader from '@/components/TableHeader'
import TableRow from '@/components/TableRow'
import TableBody from '@/components/TableBody'
import TableData from '@/components/TableData'
export default {
metaInfo() {
return {
title: `${this.__('Packages')}`,
}
},
layout: MainLayout,
components: {
TopBar,
Container,
Content,
Page,
PageHeader,
PageHeaderTitle,
PageBody,
Button,
List,
ListItem,
StatusBubble,
NotificationBadge,
SettingsLayout,
SettingsSegment,
Pagination,
Tabs,
Table,
TableHead,
TableHeader,
TableRow,
TableBody,
TableData,
},
props: {
packages: Array
},
data() {
return {
items: [
{
title: 'Overview',
to: this.route('admin.users.index'),
},
{
title: 'Create',
to: this.route('admin.users.create'),
}
],
}
},
}
</script>

View File

@@ -1,32 +0,0 @@
<template>
<ul class="-ml-4 space-y-1">
<li v-for="item in items">
<inertia-link
class="flex items-center h-10 px-4 font-medium text-medium-emphasis"
:class="{'rounded shadow text-primary bg-surface-3': item.active}"
:href="item.to"
>{{ item.title }} {{ item.route }}</inertia-link
>
</li>
</ul>
</template>
<script>
export default {
data() {
return {
items: [
{
title: this.__('Overview'),
to: this.route('admin.packages.index'),
active: this.route().current('admin.packages.index')
},
{
title: this.__('Create'),
to: this.route('admin.packages.create'),
active: this.route().current('admin.packages.create')
}
],
}
},
}
</script>

View File

@@ -1,174 +0,0 @@
<template>
<Page>
<TopBar/>
<Content>
<Container>
<PageHeader>
<template #start>
<PageHeaderTitle>{{ __('Servers') }}</PageHeaderTitle>
</template>
</PageHeader>
<PageBody>
<SettingsLayout main-col-span="col-span-4">
<template #segments>
<SettingsSegment>
<template #title>{{ __('Servers') }}</template>
<template #form>
<form class="space-y-4 pb-5 mb-5 border-b border-low-emphasis">
<FormInput :label="__('Search')" :placeholder="__('Search on server name, or the name/email of the owner of the server')" v-model="form.search" />
</form>
</template>
<template #content>
<div>
<Table caption="Server list overview">
<TableHead>
<TableRow>
<TableHeader>{{ __('Name') }}</TableHeader>
<TableHeader>{{ __('IP') }}</TableHeader>
<TableHeader>{{ __('Users') }}</TableHeader>
<TableHeader>{{ __('Max sites') }}</TableHeader>
<TableHeader>{{ __('Date') }}</TableHeader>
<TableHeader></TableHeader>
</TableRow>
</TableHead>
<TableBody>
<TableRow v-for="server in servers.data" :key="server.id">
<TableData>
<div>{{ server.name }}</div>
</TableData>
<TableData>
{{ server.ip }}
</TableData>
<TableData class="space-x-2">
<span v-if="!server.users || !server.users.length">-</span>
<inertia-link v-else class="text-primary" :href="route('admin.users.show', user.id)" :key="user.id" v-for="user in server.users">{{ user.name }}</inertia-link>
</TableData>
<TableData>
{{ server.maximum_sites }} ({{ __('Current') }}: {{ server.sites_count}})
</TableData>
<TableData>
{{ server.created_at }}
</TableData>
<TableData>
<inertia-link :href="route('admin.services.servers.edit', server.id)" class="text-primary font-medium">
{{ __('Edit') }}
</inertia-link>
</TableData>
</TableRow>
</TableBody>
</Table>
</div>
<pagination :links="servers"/>
</template>
</SettingsSegment>
</template>
</SettingsLayout>
</PageBody>
</Container>
</Content>
</Page>
</template>
<script>
import TopBar from '../components/TopBar'
import Container from '@/components/Container'
import Content from '@/components/Content'
import Page from '@/components/Page'
import PageHeader from '@/components/PageHeader'
import PageHeaderTitle from '@/components/PageHeaderTitle'
import PageBody from '@/components/PageBody'
import Button from '@/components/Button'
import List from '@/components/List'
import ListItem from '@/components/ListItem'
import StatusBubble from '@/components/StatusBubble'
import NotificationBadge from '@/components/NotificationBadge'
import MainLayout from '@/Layouts/MainLayout'
import SettingsLayout from '@/components/layouts/SettingsLayout'
import SettingsSegment from '@/components/SettingsSegment'
import Pagination from '@/components/Pagination'
import {useNotification} from '@/hooks/notification'
import FormInput from '@/components/forms/FormInput'
import Table from '@/components/Table'
import TableHead from '@/components/TableHead'
import TableHeader from '@/components/TableHeader'
import TableRow from '@/components/TableRow'
import TableBody from '@/components/TableBody'
import TableData from '@/components/TableData'
import IconPhp from '@/components/icons/IconPhp'
import throttle from "lodash/throttle";
import pickBy from "lodash/pickBy";
export default {
layout: MainLayout,
metaInfo() {
return {
title: `${this.__('Servers')}`,
}
},
components: {
TopBar,
Container,
Content,
Page,
PageHeader,
PageHeaderTitle,
PageBody,
Button,
List,
ListItem,
StatusBubble,
NotificationBadge,
SettingsLayout,
SettingsSegment,
Pagination,
Table,
TableHead,
TableHeader,
TableRow,
TableBody,
TableData,
IconPhp,
FormInput,
},
props: {
servers: Object,
filters: Object,
},
mounted() {
},
methods: {
useNotification,
},
data () {
return {
form: {
search: this.filters.search,
}
}
},
watch: {
form: {
handler: throttle(function() {
let query = pickBy(this.form)
this.$inertia.get(this.route('admin.servers.index', Object.keys(query).length ? query : { remember: 'forget' }), {},{
preserveScroll: true,
preserveState: true,
})
}, 500),
deep: true
},
},
}
</script>

View File

@@ -1,211 +0,0 @@
<template>
<Page>
<TopBar/>
<Content>
<Container>
<PageHeader>
<template #start>
<PageHeaderTitle>{{ __('Services') }}</PageHeaderTitle>
</template>
</PageHeader>
<PageBody>
<SettingsLayout>
<template #nav>
<Tabs />
</template>
<template #segments>
<SettingsSegment>
<template #title>{{ __('Servers') }}</template>
<template #content>
<div>
<Table caption="Server list overview">
<TableHead>
<TableRow>
<TableHeader>{{ __('Name') }}</TableHeader>
<TableHeader>{{ __('Usage') }}</TableHeader>
<TableHeader>{{ __('Users') }}</TableHeader>
<TableHeader></TableHeader>
</TableRow>
</TableHead>
<TableBody>
<TableRow v-for="server in servers.data" :key="server.id">
<TableData>
{{ server.name }}
<p class="text-medium-emphasis">{{ server.ip }}</p>
<Button class="text-small text-primary" is="a" size="sm" target="_blank" :href="`https://ploi.io/panel/servers/${server.ploi_id}`">
{{ __('View in ploi.io') }}
</Button>
</TableData>
<TableData>{{ server.sites_count }}/{{ server.maximum_sites }}</TableData>
<TableData>{{ server.users_count }}</TableData>
<TableData>
<inertia-link :href="route('admin.services.servers.edit', server.id)" class="text-primary font-medium">
{{ __('Edit') }}
</inertia-link>
</TableData>
</TableRow>
</TableBody>
</Table>
</div>
<pagination :links="servers"/>
</template>
</SettingsSegment>
<SettingsSegment>
<template #title>{{ __('Sites') }}</template>
<template #content>
<div>
<Table caption="Site list overview">
<TableHead>
<TableRow>
<TableHeader>{{ __('Name') }}</TableHeader>
<TableHeader>{{ __('Server') }}</TableHeader>
<TableHeader>{{ __('Users') }}</TableHeader>
<TableHeader></TableHeader>
</TableRow>
</TableHead>
<TableBody>
<TableRow v-for="site in sites.data" :key="site.id">
<TableData>
{{ site.domain }}
</TableData>
<TableData>
{{ site.server ? site.server.name : '-' }}
</TableData>
<TableData>
{{ site.users_count }}
</TableData>
<TableData>
<inertia-link :href="route('admin.services.sites.edit', site.id)" class="text-primary font-medium">
{{ __('Edit') }}
</inertia-link>
</TableData>
</TableRow>
</TableBody>
</Table>
</div>
<pagination :links="sites"/>
</template>
</SettingsSegment>
<SettingsSegment>
<template #title>{{ __('Providers') }}</template>
<template #content>
<div>
<Table caption="Provider list overview">
<TableHead>
<TableRow>
<TableHeader>{{ __('Name') }}</TableHeader>
<TableHeader>{{ __('Regions') }}</TableHeader>
<TableHeader>{{ __('Plans') }}</TableHeader>
<TableHeader>{{ __('Attached servers') }}</TableHeader>
<TableHeader></TableHeader>
</TableRow>
</TableHead>
<TableBody>
<TableRow v-for="provider in providers.data" :key="provider.id">
<TableData>{{ provider.name }}</TableData>
<TableData>{{ provider.regions_count }}</TableData>
<TableData>{{ provider.plans_count }}</TableData>
<TableData>{{ provider.servers_count }}</TableData>
<TableData>
<inertia-link :href="route('admin.services.providers.edit', provider.id)" class="text-primary font-medium">
{{ __('Edit') }}
</inertia-link>
</TableData>
</TableRow>
</TableBody>
</Table>
</div>
<pagination :links="providers"/>
</template>
</SettingsSegment>
</template>
</SettingsLayout>
</PageBody>
</Container>
</Content>
</Page>
</template>
<script>
import TopBar from '../components/TopBar'
import Container from '@/components/Container'
import Content from '@/components/Content'
import Page from '@/components/Page'
import PageHeader from '@/components/PageHeader'
import PageHeaderTitle from '@/components/PageHeaderTitle'
import PageBody from '@/components/PageBody'
import Button from '@/components/Button'
import List from '@/components/List'
import ListItem from '@/components/ListItem'
import StatusBubble from '@/components/StatusBubble'
import NotificationBadge from '@/components/NotificationBadge'
import MainLayout from '@/Layouts/MainLayout'
import SettingsLayout from '@/components/layouts/SettingsLayout'
import SettingsSegment from '@/components/SettingsSegment'
import Pagination from '@/components/Pagination'
import {useNotification} from '@/hooks/notification'
import Tabs from './Tabs';
import Table from '@/components/Table'
import TableHead from '@/components/TableHead'
import TableHeader from '@/components/TableHeader'
import TableRow from '@/components/TableRow'
import TableBody from '@/components/TableBody'
import TableData from '@/components/TableData'
export default {
layout: MainLayout,
metaInfo() {
return {
title: `${this.__('Services')}`,
}
},
components: {
TopBar,
Container,
Content,
Page,
PageHeader,
PageHeaderTitle,
PageBody,
Button,
List,
ListItem,
StatusBubble,
NotificationBadge,
SettingsLayout,
SettingsSegment,
Pagination,
Tabs,
Table,
TableHead,
TableHeader,
TableRow,
TableBody,
TableData,
},
props: {
servers: Object,
sites: Object,
providers: Object,
},
mounted() {
},
methods: {
useNotification,
},
}
</script>

View File

@@ -1,186 +0,0 @@
<template>
<Page>
<TopBar/>
<Content>
<Container>
<PageHeader>
<template #start>
<PageHeaderTitle>{{ __('Edit provider') }}</PageHeaderTitle>
</template>
</PageHeader>
<PageBody>
<SettingsLayout>
<template #nav>
<Tabs />
</template>
<template #segments>
<SettingsSegment>
<template #title>{{ __('Edit') }}</template>
<template #form>
<form class="space-y-4" @submit.prevent="submit">
<FormInput :label="__('Name')" :errors="$page.props.errors.name" v-model="form.name" />
<div class="space-y-4">
<h3 class="text-base leading-6 font-medium border-b border-dotted border-medium-emphasis pb-1">
{{ __('Allowed plans') }}</h3>
<p class="text-medium-emphasis">
Select the plans here that your users are allowed to use for this provider.
If there are no plans selected, all will be available.
</p>
<div>
<div class="space-y-1" v-for="(name, id) in availablePlans">
<input :id="`plan-${id}`" :value="id" v-model="form.allowed_plans"
class="form-checkbox" type="checkbox">
<label :for="`plan-${id}`" class="ml-2 text-sm">{{ name }}</label>
</div>
</div>
</div>
<div class="space-y-4">
<h3 class="text-base leading-6 font-medium border-b border-dotted border-medium-emphasis pb-1">
{{ __('Allowed regions') }}</h3>
<p class="text-medium-emphasis">
Select the regions here that your users are allowed to use for this provider.
If there are no regions selected, all will be available.
</p>
<div>
<div class="space-y-1" v-for="(name, id) in availableRegions">
<input :id="`region-${id}`" :value="id" v-model="form.allowed_regions"
class="form-checkbox" type="checkbox">
<label :for="`region-${id}`" class="ml-2 text-sm">{{ name }}</label>
</div>
</div>
</div>
<FormActions>
<Button>{{ __('Save') }}</Button>
<Button variant="danger" type="button" @click="confirmDelete">{{ __('Delete') }}</Button>
</FormActions>
</form>
</template>
</SettingsSegment>
</template>
</SettingsLayout>
</PageBody>
</Container>
</Content>
</Page>
</template>
<script>
import TopBar from '../../components/TopBar'
import Container from '@/components/Container'
import Content from '@/components/Content'
import Page from '@/components/Page'
import PageHeader from '@/components/PageHeader'
import PageHeaderTitle from '@/components/PageHeaderTitle'
import PageBody from '@/components/PageBody'
import Button from '@/components/Button'
import List from '@/components/List'
import ListItem from '@/components/ListItem'
import StatusBubble from '@/components/StatusBubble'
import NotificationBadge from '@/components/NotificationBadge'
import MainLayout from '@/Layouts/MainLayout'
import SettingsLayout from '@/components/layouts/SettingsLayout'
import SettingsSegment from '@/components/SettingsSegment'
import FormInput from '@/components/forms/FormInput'
import FormTextarea from '@/components/forms/FormTextarea'
import FormSelect from '@/components/forms/FormSelect'
import Form from '@/components/Form'
import FormActions from '@/components/FormActions'
import {useConfirm} from '@/hooks/confirm'
import Tabs from '../Tabs'
import Table from '@/components/Table'
import TableHead from '@/components/TableHead'
import TableHeader from '@/components/TableHeader'
import TableRow from '@/components/TableRow'
import TableBody from '@/components/TableBody'
import TableData from '@/components/TableData'
export default {
layout: MainLayout,
metaInfo() {
return {
title: `${this.__('Edit provider')}`,
}
},
components: {
TopBar,
Container,
Content,
Page,
PageHeader,
PageHeaderTitle,
PageBody,
Button,
List,
ListItem,
StatusBubble,
NotificationBadge,
FormInput,
FormSelect,
SettingsLayout,
SettingsSegment,
Form,
FormActions,
Tabs,
FormTextarea,
Table,
TableHead,
TableHeader,
TableRow,
TableBody,
TableData,
},
data() {
return {
sending: false,
form: {
name: this.provider.name,
allowed_plans: this.provider.allowed_plans ?? [],
allowed_regions: this.provider.allowed_regions ?? [],
},
}
},
props: {
provider: Object,
availableRegions: [Object, Array],
availablePlans: [Object, Array]
},
methods: {
submit() {
this.$inertia.patch(this.route('admin.services.providers.update', this.provider.id), this.form, {
onStart: () => this.sending = true,
onFinish: () => this.sending = false
});
},
confirmDelete() {
useConfirm({
title: this.__('Are you sure?'),
message: this.__('Are you sure you want to delete this provider?'),
onConfirm: () => this.delete(),
})
},
delete() {
this.$inertia.delete(this.route('admin.services.providers.delete', this.provider.id), {
onStart: () => this.sending = true,
onFinish: () => this.sending = false
})
},
}
}
</script>

View File

@@ -1,142 +0,0 @@
<template>
<Page>
<TopBar/>
<Content>
<Container>
<PageHeader>
<template #start>
<PageHeaderTitle>{{ __('Synchronize providers') }}</PageHeaderTitle>
</template>
</PageHeader>
<PageBody>
<SettingsLayout>
<template #nav>
<Tabs />
</template>
<template #segments>
<SettingsSegment>
<template #title>{{ __('Overview') }}</template>
<template #subtitle>{{ __('You can synchronize your server providers here. After that, you\'ll be able to set a default provider per package. So servers created by a user via a package will always be put on that selected provider.') }}</template>
<template #content>
<Table caption="Available providers overview">
<TableHead>
<TableRow>
<TableHeader>{{ __('Name') }}</TableHeader>
<TableHeader>{{ __('Label') }}</TableHeader>
<TableHeader></TableHeader>
</TableRow>
</TableHead>
<TableBody>
<TableRow v-for="availableProvider in availableProviders" :key="availableProvider.id">
<TableData class="space-y-2">
<div>
{{ availableProvider.name }}
</div>
<div class="space-x-1 text-xs">
<span class="text-medium-emphasis">{{ availableProvider.provider.plans.length }} plan(s)</span>
<span>&centerdot;</span>
<span class="text-medium-emphasis">{{ availableProvider.provider.regions.length }} region(s)</span>
</div>
</TableData>
<TableData>{{ availableProvider.label }}</TableData>
<TableData class="text-right">
<Button size="sm" v-on:click="syncProvider(availableProvider)">
{{ __('Synchronize') }}
</Button>
</TableData>
</TableRow>
</TableBody>
</Table>
</template>
</SettingsSegment>
</template>
</SettingsLayout>
</PageBody>
</Container>
</Content>
</Page>
</template>
<script>
import TopBar from '../components/TopBar'
import Container from '@/components/Container'
import Content from '@/components/Content'
import Page from '@/components/Page'
import PageHeader from '@/components/PageHeader'
import PageHeaderTitle from '@/components/PageHeaderTitle'
import PageBody from '@/components/PageBody'
import Button from '@/components/Button'
import List from '@/components/List'
import ListItem from '@/components/ListItem'
import StatusBubble from '@/components/StatusBubble'
import NotificationBadge from '@/components/NotificationBadge'
import MainLayout from '@/Layouts/MainLayout'
import SettingsLayout from '@/components/layouts/SettingsLayout'
import SettingsSegment from '@/components/SettingsSegment'
import Pagination from '@/components/Pagination'
import {useNotification} from '@/hooks/notification'
import Tabs from './Tabs';
import Table from '@/components/Table'
import TableHead from '@/components/TableHead'
import TableHeader from '@/components/TableHeader'
import TableRow from '@/components/TableRow'
import TableBody from '@/components/TableBody'
import TableData from '@/components/TableData'
export default {
layout: MainLayout,
metaInfo() {
return {
title: `${this.__('Synchronize providers')}`,
}
},
components: {
TopBar,
Container,
Content,
Page,
PageHeader,
PageHeaderTitle,
PageBody,
Button,
List,
ListItem,
StatusBubble,
NotificationBadge,
SettingsLayout,
SettingsSegment,
Pagination,
Tabs,
Table,
TableHead,
TableHeader,
TableRow,
TableBody,
TableData,
},
props: {
availableProviders: Array,
},
methods: {
useNotification,
syncProvider (provider){
window.axios.post(this.route('admin.services.providers.sync', provider.id))
.then(() => {
useNotification({
variant: 'success',
title: `Providers`,
message: `Provider has been synchronized to this system`
})
});
}
},
}
</script>

View File

@@ -1,220 +0,0 @@
<template>
<Page>
<TopBar/>
<Content>
<Container>
<PageHeader>
<template #start>
<PageHeaderTitle>{{ __('Edit server') }}</PageHeaderTitle>
</template>
</PageHeader>
<PageBody>
<SettingsLayout>
<template #nav>
<Tabs/>
</template>
<template #segments>
<SettingsSegment>
<template #title>{{ __('Edit') }}</template>
<template #form>
<form class="space-y-4" @submit.prevent="submit">
<FormInput :label="__('Name')" :errors="$page.props.errors.name"
v-model="form.name"/>
<FormInput :label="__('IP address')" :errors="$page.props.errors.ip"
v-model="form.ip"/>
<FormInput :label="__('Maximum sites')"
:errors="$page.props.errors.maximum_sites"
v-model="form.maximum_sites"/>
<FormActions>
<Button>{{ __('Save') }}</Button>
<Button variant="danger" type="button" @click="confirmDelete">
{{ __('Delete') }}
</Button>
</FormActions>
</form>
</template>
</SettingsSegment>
<SettingsSegment>
<template #title>{{ __('Attached users') }}</template>
<template #form>
<form class="space-y-4" @submit.prevent="attach">
<FormInput :label="__('Email')" :errors="$page.props.errors.email"
v-model="formAttach.email"/>
<FormActions>
<Button>{{ __('Save') }}</Button>
</FormActions>
</form>
</template>
<template #content>
<div>
<Table caption="Attached users list overview">
<TableHead>
<TableRow>
<TableHeader>{{ __('Name') }}</TableHeader>
<TableHeader>{{ __('Email') }}</TableHeader>
<TableHeader></TableHeader>
</TableRow>
</TableHead>
<TableBody>
<TableRow v-for="user in users" :key="user.id">
<TableData>{{ user.name }}</TableData>
<TableData>{{ user.email }}</TableData>
<TableData>
<Button variant="danger" size="sm"
@click="confirmDetach(user.id)">
{{ __('Detach') }}
</Button>
</TableData>
</TableRow>
</TableBody>
</Table>
</div>
</template>
</SettingsSegment>
</template>
</SettingsLayout>
</PageBody>
</Container>
</Content>
</Page>
</template>
<script>
import TopBar from '../../components/TopBar'
import Container from '@/components/Container'
import Content from '@/components/Content'
import Page from '@/components/Page'
import PageHeader from '@/components/PageHeader'
import PageHeaderTitle from '@/components/PageHeaderTitle'
import PageBody from '@/components/PageBody'
import Button from '@/components/Button'
import List from '@/components/List'
import ListItem from '@/components/ListItem'
import StatusBubble from '@/components/StatusBubble'
import NotificationBadge from '@/components/NotificationBadge'
import MainLayout from '@/Layouts/MainLayout'
import SettingsLayout from '@/components/layouts/SettingsLayout'
import SettingsSegment from '@/components/SettingsSegment'
import FormInput from '@/components/forms/FormInput'
import FormTextarea from '@/components/forms/FormTextarea'
import FormSelect from '@/components/forms/FormSelect'
import Form from '@/components/Form'
import FormActions from '@/components/FormActions'
import {useConfirm} from '@/hooks/confirm'
import Tabs from '../Tabs'
import Table from '@/components/Table'
import TableHead from '@/components/TableHead'
import TableHeader from '@/components/TableHeader'
import TableRow from '@/components/TableRow'
import TableBody from '@/components/TableBody'
import TableData from '@/components/TableData'
export default {
layout: MainLayout,
components: {
TopBar,
Container,
Content,
Page,
PageHeader,
PageHeaderTitle,
PageBody,
Button,
List,
ListItem,
StatusBubble,
NotificationBadge,
FormInput,
FormSelect,
SettingsLayout,
SettingsSegment,
Form,
FormActions,
Tabs,
FormTextarea,
Table,
TableHead,
TableHeader,
TableRow,
TableBody,
TableData,
},
data() {
return {
sending: false,
form: {
name: this.server.name,
ip: this.server.ip,
maximum_sites: this.server.maximum_sites,
},
formAttach: {
email: null
}
}
},
props: {
server: Object,
users: [Object, Array]
},
methods: {
submit() {
this.$inertia.patch(this.route('admin.services.servers.update', this.server.id), this.form, {
onStart: () => this.sending = true,
onFinish: () => this.sending = false
})
},
confirmDelete() {
useConfirm({
title: this.__('Are you sure?'),
message: this.__('Are you sure you want to delete this server? Everything associated with this server will be detached.'),
onConfirm: () => this.delete(),
})
},
delete() {
this.$inertia.delete(this.route('admin.services.servers.delete', this.server.id), {
onStart: () => this.sending = true,
onFinish: () => this.sending = false
})
},
attach() {
this.$inertia.post(this.route('admin.services.servers.attach', this.server.id), this.formAttach, {
onStart: () => this.sending = true,
onFinish: () => {
this.sending = false
if (!Object.keys(this.$page.props.errors).length) {
this.formAttach.email = null;
}
}
})
},
confirmDetach(userId) {
useConfirm({
title: this.__('Are you sure?'),
message: this.__('Are you sure you want to detach this user from this server?'),
onConfirm: () => this.detach(userId),
})
},
detach(userId) {
this.$inertia.delete(this.route('admin.services.servers.detach', [this.server.id, userId]), {
onStart: () => this.sending = true,
onFinish: () => this.sending = false
})
}
}
}
</script>

View File

@@ -1,171 +0,0 @@
<template>
<Page>
<TopBar/>
<Content>
<Container>
<PageHeader>
<template #start>
<PageHeaderTitle>{{ __('Synchronize servers') }}</PageHeaderTitle>
</template>
</PageHeader>
<PageBody>
<SettingsLayout>
<template #nav>
<Tabs/>
</template>
<template #segments>
<SettingsSegment>
<template #title>{{ __('Overview') }}</template>
<template #subtitle>{{
__('You can synchronize your servers here. It is safe to synchronize already existing servers. If you have installed a extra PHP version for example, you can synchronize to update the versions here.')
}}
</template>
<template #content>
<Button size="sm" :disabled="loading" v-on:click="syncAll">
{{ __('Synchronize all servers') }}
</Button>
<Table caption="Available servers overview">
<TableHead>
<TableRow>
<TableHeader>{{ __('Name') }}</TableHeader>
<TableHeader>{{ __('IP address') }}</TableHeader>
<TableHeader></TableHeader>
</TableRow>
</TableHead>
<TableBody>
<TableRow v-for="availableServer in availableServers"
:key="availableServer.id">
<TableData>{{ availableServer.name }}</TableData>
<TableData>{{ availableServer.ip_address }}</TableData>
<TableData class="flex justify-end">
<Button :disabled="loading" size="sm"
v-on:click="syncServer(availableServer)">
{{ __('Synchronize') }}
</Button>
</TableData>
</TableRow>
</TableBody>
</Table>
</template>
</SettingsSegment>
</template>
</SettingsLayout>
</PageBody>
</Container>
</Content>
</Page>
</template>
<script>
import TopBar from '../components/TopBar'
import Container from '@/components/Container'
import Content from '@/components/Content'
import Page from '@/components/Page'
import PageHeader from '@/components/PageHeader'
import PageHeaderTitle from '@/components/PageHeaderTitle'
import PageBody from '@/components/PageBody'
import Button from '@/components/Button'
import List from '@/components/List'
import ListItem from '@/components/ListItem'
import StatusBubble from '@/components/StatusBubble'
import NotificationBadge from '@/components/NotificationBadge'
import MainLayout from '@/Layouts/MainLayout'
import SettingsLayout from '@/components/layouts/SettingsLayout'
import SettingsSegment from '@/components/SettingsSegment'
import Pagination from '@/components/Pagination'
import {useNotification} from '@/hooks/notification'
import Tabs from './Tabs';
import Table from '@/components/Table'
import TableHead from '@/components/TableHead'
import TableHeader from '@/components/TableHeader'
import TableRow from '@/components/TableRow'
import TableBody from '@/components/TableBody'
import TableData from '@/components/TableData'
export default {
layout: MainLayout,
metaInfo() {
return {
title: `${this.__('Synchronize servers')}`,
}
},
components: {
TopBar,
Container,
Content,
Page,
PageHeader,
PageHeaderTitle,
PageBody,
Button,
List,
ListItem,
StatusBubble,
NotificationBadge,
SettingsLayout,
SettingsSegment,
Pagination,
Tabs,
Table,
TableHead,
TableHeader,
TableRow,
TableBody,
TableData,
},
props: {
availableServers: Array,
},
data() {
return {
loading: false,
}
},
methods: {
useNotification,
syncServer(server) {
this.loading = true;
window.axios.post(this.route('admin.services.servers.sync'), server)
.then(() => {
this.loading = false;
useNotification({
variant: 'success',
title: `Servers`,
message: `Server ${server.name} has been synchronized to this system`
})
})
.catch(error => {
this.loading = false;
})
},
syncAll() {
this.loading = true;
window.axios.post(this.route('admin.services.servers.sync.all'))
.then(() => {
this.loading = false;
useNotification({
variant: 'success',
title: `Servers`,
message: `All servers have been synchronized to this system`
})
})
.catch(error => {
this.loading = false;
})
}
},
}
</script>

View File

@@ -1,219 +0,0 @@
<template>
<Page>
<TopBar/>
<Content>
<Container>
<PageHeader>
<template #start>
<PageHeaderTitle>{{ __('Edit site') }}</PageHeaderTitle>
</template>
</PageHeader>
<PageBody>
<SettingsLayout>
<template #nav>
<Tabs />
</template>
<template #segments>
<SettingsSegment>
<template #title>{{ __('Edit') }}</template>
<template #form>
<form class="space-y-4" @submit.prevent="submit">
<FormInput :label="__('Domain')" :errors="$page.props.errors.domain" v-model="form.domain" />
<FormActions>
<Button>{{ __('Save') }}</Button>
<Button variant="danger" type="button" @click="confirmDelete">{{ __('Delete') }}</Button>
</FormActions>
</form>
</template>
</SettingsSegment>
<SettingsSegment>
<template #title>{{ __('Attached users') }}</template>
<template #form>
<form class="space-y-4" @submit.prevent="attach">
<FormInput :label="__('Email')" :errors="$page.props.errors.email" v-model="formAttach.email"/>
<FormActions>
<Button>{{ __('Save') }}</Button>
</FormActions>
</form>
</template>
<template #content>
<div>
<Table caption="Attached users list overview">
<TableHead>
<TableRow>
<TableHeader>{{ __('Name') }}</TableHeader>
<TableHeader>{{ __('Email') }}</TableHeader>
<TableHeader></TableHeader>
</TableRow>
</TableHead>
<TableBody>
<TableRow v-for="user in users" :key="user.id">
<TableData>{{ user.name }}</TableData>
<TableData>{{ user.email }}</TableData>
<TableData>
<Button variant="danger" size="sm"
@click="confirmDetach(user.id)">
{{ __('Detach') }}
</Button>
</TableData>
</TableRow>
</TableBody>
</Table>
</div>
</template>
</SettingsSegment>
</template>
</SettingsLayout>
</PageBody>
</Container>
</Content>
</Page>
</template>
<script>
import TopBar from '../../components/TopBar'
import Container from '@/components/Container'
import Content from '@/components/Content'
import Page from '@/components/Page'
import PageHeader from '@/components/PageHeader'
import PageHeaderTitle from '@/components/PageHeaderTitle'
import PageBody from '@/components/PageBody'
import Button from '@/components/Button'
import List from '@/components/List'
import ListItem from '@/components/ListItem'
import StatusBubble from '@/components/StatusBubble'
import NotificationBadge from '@/components/NotificationBadge'
import MainLayout from '@/Layouts/MainLayout'
import SettingsLayout from '@/components/layouts/SettingsLayout'
import SettingsSegment from '@/components/SettingsSegment'
import FormInput from '@/components/forms/FormInput'
import FormTextarea from '@/components/forms/FormTextarea'
import FormSelect from '@/components/forms/FormSelect'
import Form from '@/components/Form'
import FormActions from '@/components/FormActions'
import {useConfirm} from '@/hooks/confirm'
import Tabs from '../Tabs'
import Table from '@/components/Table'
import TableHead from '@/components/TableHead'
import TableHeader from '@/components/TableHeader'
import TableRow from '@/components/TableRow'
import TableBody from '@/components/TableBody'
import TableData from '@/components/TableData'
export default {
layout: MainLayout,
components: {
TopBar,
Container,
Content,
Page,
PageHeader,
PageHeaderTitle,
PageBody,
Button,
List,
ListItem,
StatusBubble,
NotificationBadge,
FormInput,
FormSelect,
SettingsLayout,
SettingsSegment,
Form,
FormActions,
Tabs,
FormTextarea,
Table,
TableHead,
TableHeader,
TableRow,
TableBody,
TableData,
},
data() {
return {
sending: false,
form: {
domain: this.site.domain
},
formAttach: {
email: null
}
}
},
props: {
site: Object,
users: [Object, Array]
},
methods: {
submit() {
this.sending = true
this.$inertia.patch(this.route('admin.services.sites.update', this.site.id), this.form, {
onFinish: () => {
this.sending = false;
}
})
},
confirmDelete() {
useConfirm({
title: this.__('Are you sure?'),
message: this.__('Are you sure you want to delete this site? Everything associated with this site will be detached.'),
onConfirm: () => this.delete(),
})
},
delete() {
this.sending = true
this.$inertia.delete(this.route('admin.services.sites.delete', this.site.id), {
onFinish: () => {
this.sending = false;
}
})
},
attach() {
this.sending = true
this.$inertia.post(this.route('admin.services.sites.attach', this.site.id), this.formAttach, {
onFinish: () => {
this.sending = false
if (!Object.keys(this.$page.props.errors).length) {
this.formAttach.email = null;
}
}
});
},
confirmDetach(userId) {
useConfirm({
title: this.__('Are you sure?'),
message: this.__('Are you sure you want to detach this user from this site?'),
onConfirm: () => this.detach(userId),
})
},
detach(userId) {
this.sending = true
this.$inertia.delete(this.route('admin.services.sites.detach', [this.site.id, userId]), {
onFinish: () => {
this.sending = false;
}
})
}
}
}
</script>

View File

@@ -1,159 +0,0 @@
<template>
<Page>
<TopBar/>
<Content>
<Container>
<PageHeader>
<template #start>
<PageHeaderTitle>Synchronize sites</PageHeaderTitle>
</template>
</PageHeader>
<PageBody>
<SettingsLayout>
<template #nav>
<Tabs />
</template>
<template #segments>
<SettingsSegment>
<template #title>{{ __('Overview') }}</template>
<template #subtitle>{{ __('You can synchronize your sites here. It is safe to synchronize already existing sites.') }}</template>
<template #content>
<Button size="sm" :disabled="loading" v-on:click="syncAll">
{{ __('Synchronize all sites') }}
</Button>
<Table caption="Available sites overview">
<TableHead>
<TableRow>
<TableHeader>{{ __('Domain') }}</TableHeader>
<TableHeader></TableHeader>
</TableRow>
</TableHead>
<TableBody>
<TableRow v-for="availableSite in availableSites" :key="availableSite.id">
<TableData>{{ availableSite.domain }}</TableData>
<TableData class="flex justify-end">
<Button :disabled="loading" size="sm" v-on:click="syncSite(availableSite)">
{{ __('Synchronize') }}
</Button>
</TableData>
</TableRow>
</TableBody>
</Table>
</template>
</SettingsSegment>
</template>
</SettingsLayout>
</PageBody>
</Container>
</Content>
</Page>
</template>
<script>
import TopBar from '../components/TopBar'
import Container from '@/components/Container'
import Content from '@/components/Content'
import Page from '@/components/Page'
import PageHeader from '@/components/PageHeader'
import PageHeaderTitle from '@/components/PageHeaderTitle'
import PageBody from '@/components/PageBody'
import Button from '@/components/Button'
import List from '@/components/List'
import ListItem from '@/components/ListItem'
import StatusBubble from '@/components/StatusBubble'
import NotificationBadge from '@/components/NotificationBadge'
import MainLayout from '@/Layouts/MainLayout'
import SettingsLayout from '@/components/layouts/SettingsLayout'
import SettingsSegment from '@/components/SettingsSegment'
import Pagination from '@/components/Pagination'
import {useNotification} from '@/hooks/notification'
import Tabs from './Tabs';
import Table from '@/components/Table'
import TableHead from '@/components/TableHead'
import TableHeader from '@/components/TableHeader'
import TableRow from '@/components/TableRow'
import TableBody from '@/components/TableBody'
import TableData from '@/components/TableData'
export default {
layout: MainLayout,
components: {
TopBar,
Container,
Content,
Page,
PageHeader,
PageHeaderTitle,
PageBody,
Button,
List,
ListItem,
StatusBubble,
NotificationBadge,
SettingsLayout,
SettingsSegment,
Pagination,
Tabs,
Table,
TableHead,
TableHeader,
TableRow,
TableBody,
TableData,
},
props: {
availableSites: Array,
},
data() {
return {
loading: false,
}
},
methods: {
useNotification,
syncSite (site){
this.loading = true;
window.axios.post(this.route('admin.services.sites.sync'), site)
.then(() => {
this.loading = false;
useNotification({
variant: 'success',
title: `Sites`,
message: `Site ${site.domain} has been synchronized to this system`
})
})
.catch(error => {
this.loading = false;
})
},
syncAll() {
this.loading = true;
window.axios.post(this.route('admin.services.sites.sync.all'))
.then(() => {
this.loading = false;
useNotification({
variant: 'success',
title: `Sites`,
message: `All sites have been synchronized to this system`
})
})
.catch(error => {
this.loading = false;
})
}
},
}
</script>

View File

@@ -1,42 +0,0 @@
<template>
<ul class="-ml-4 space-y-1">
<li v-for="item in items">
<inertia-link
class="flex items-center h-10 px-4 font-medium text-medium-emphasis"
:class="{'rounded shadow text-primary bg-surface-3': item.active}"
:href="item.to"
>{{ item.title }} {{ item.route }}</inertia-link
>
</li>
</ul>
</template>
<script>
export default {
data() {
return {
items: [
{
title: this.__('Overview'),
to: this.route('admin.services.index'),
active: this.route().current('admin.services.index')
},
{
title: this.__('Synchronize servers'),
to: this.route('admin.services.servers.index'),
active: this.route().current('admin.services.servers.index')
},
{
title: this.__('Synchronize sites'),
to: this.route('admin.services.sites.index'),
active: this.route().current('admin.services.sites.index')
},
{
title: this.__('Synchronize providers'),
to: this.route('admin.services.providers.index'),
active: this.route().current('admin.services.providers.index')
},
],
}
},
}
</script>

View File

@@ -1,345 +0,0 @@
<template>
<Page>
<TopBar/>
<Content>
<Container>
<PageHeader>
<template #start>
<PageHeaderTitle>{{ __('Settings') }}</PageHeaderTitle>
</template>
</PageHeader>
<PageBody>
<SettingsLayout>
<template #nav>
<Tabs/>
</template>
<template #segments>
<SettingsSegment>
<template #title>{{ __('Overview') }}</template>
<template #subtitle>
{{
__('Change all the system settings here. All changes are in effect immediately.')
}}
</template>
<template #form>
<form class="space-y-4" @submit.prevent="submit">
<FormInput :label="__('Company name')" :errors="$page.props.errors.name"
v-model="form.name"/>
<FormInput :label="__('E-mail address')" :errors="$page.props.errors.email"
v-model="form.email"/>
<FormFileInput accept="image/*" :label="__('Logo')" type="file"
:errors="$page.props.errors.logo"
v-model="form.logo"/>
<Button v-if="company_settings.has_logo" variant="danger" type="button"
class="ml-2 px-4 py-1 bg-red-500 hover:bg-red-700 rounded-sm text-xs font-medium text-white"
@click="removeLogo">
Remove logo
</Button>
<FormInput :helper-text="__('Separate by comma to allow more email addresses')"
:label="__('Support email addresses')"
:errors="$page.props.errors.support_emails"
v-model="form.support_emails"/>
<FormSelect
:helper-text="__('Select the default package a user should get when you create or they register')"
:label="__('Select default package')" v-model="form.default_package">
<option v-for="(name, id) in packages" :value="id" v-text="name"></option>
</FormSelect>
<FormSelect
:helper-text="__('Select the default package a user should get when you create or they register')"
:label="__('Select default language')" v-model="form.default_language">
<option v-for="language in languages" :value="language"
v-text="language"></option>
</FormSelect>
<FormSelect
:helper-text="__('This will rotate any logs older than selected, this helps cleanup your database')"
:label="__('Rotate logs after')" v-model="form.rotate_logs_after">
<option value="">Don't rotate logs</option>
<option value="weeks-1">Older than 1 week</option>
<option value="months-1">Older than 1 month</option>
<option value="months-3">Older than 3 months</option>
<option value="months-6">Older than 6 months</option>
<option value="years-1">Older than 1 year</option>
<option value="years-2">Older than 2 years</option>
<option value="years-3">Older than 3 years</option>
<option value="years-4">Older than 4 years</option>
</FormSelect>
<div>
<input id="trial" class="form-checkbox" type="checkbox"
v-model="form.trialEnabled">
<label for="trial" class="ml-2 text-sm">{{
__('Enable trial')
}}</label>
<p class="text-small mt-1 text-medium-emphasis">
{{ __('This will allow you to have users with trials.') }}
</p>
</div>
<FormInput v-if="form.trialEnabled" type="number" :label="__('Trial days')"
:errors="$page.props.errors.trial"
v-model="form.trial"/>
<FormSelect
v-if="form.trialEnabled"
:errors="$page.props.errors.trial_package"
:helper-text="__('Select the trial package a user should get when they get their trial')"
:label="__('Select trial package')" v-model="form.trial_package">
<option v-for="(name, id) in packages" :value="id" v-text="name"></option>
</FormSelect>
<div>
<input id="support" class="form-checkbox" type="checkbox"
v-model="form.support">
<label for="support" class="ml-2 text-sm">{{
__('Enable support platform')
}}</label>
<p class="text-small mt-1 text-medium-emphasis">
{{
__('This will allow your customers to make support requests to you.')
}}
</p>
</div>
<div>
<input id="documentation" class="form-checkbox" type="checkbox"
v-model="form.documentation">
<label for="documentation" class="ml-2 text-sm">{{
__('Enable documentation platform')
}}</label>
<p class="text-small mt-1 text-medium-emphasis">
{{
__('This will allow you to create articles for your users to look at.')
}}
</p>
</div>
<div>
<input id="allow_registration" class="form-checkbox" type="checkbox"
v-model="form.allow_registration">
<label for="allow_registration"
class="ml-2 text-sm">{{ __('Allow customer registration') }}</label>
<p class="text-small mt-1 text-medium-emphasis">
{{ __('This will allow public users to register on your platform.') }}
</p>
</div>
<div>
<input id="receive_email_on_server_creation" class="form-checkbox"
type="checkbox"
v-model="form.receive_email_on_server_creation">
<label for="receive_email_on_server_creation" class="ml-2 text-sm">{{
__('Receive email when customers create server')
}}</label>
<p class="text-small mt-1 text-medium-emphasis">
{{
__('This will send an email to all admins notifying them about a new server installation.')
}}
</p>
</div>
<div>
<input id="receive_email_on_site_creation" class="form-checkbox"
type="checkbox"
v-model="form.receive_email_on_site_creation">
<label for="receive_email_on_site_creation" class="ml-2 text-sm">{{
__('Receive email when customers create site')
}}</label>
<p class="text-small mt-1 text-medium-emphasis">
{{
__('This will send an email to all admins notifying them about a new site installation.')
}}
</p>
</div>
<div>
<input id="enable_api" class="form-checkbox" type="checkbox"
v-model="form.enable_api">
<label for="enable_api" class="ml-2 text-sm">{{ __('Enable API') }}</label>
<p class="text-small mt-1 text-medium-emphasis">
{{
__('This will allow you to interact with your system via the API.')
}} <a href="https://docs.ploi-core.io/core-api/introduction"
class="text-primary"
target="_blank">{{ __('More information') }}</a>
</p>
</div>
<FormInput v-if="form.enable_api" allow-random-string :label="__('API token')"
:errors="$page.props.errors.api_token"
v-model="form.api_token"/>
<div>
<input id="isolate_per_site_per_user" class="form-checkbox" type="checkbox"
v-model="form.isolate_per_site_per_user">
<label for="isolate_per_site_per_user" class="ml-2 text-sm">{{
__('Enable site isolation per site & user')
}}</label>
<p class="text-small mt-1 text-medium-emphasis">
{{
__('This will make sure each site created by one user is always isolated from another.')
}}
</p>
</div>
<FormActions>
<Button>{{ __('Save') }}</Button>
</FormActions>
</form>
</template>
</SettingsSegment>
</template>
</SettingsLayout>
</PageBody>
</Container>
</Content>
</Page>
</template>
<script>
import TopBar from './components/TopBar.vue'
import Container from '@/components/Container.vue'
import Content from '@/components/Content.vue'
import Page from '@/components/Page.vue'
import PageHeader from '@/components/PageHeader.vue'
import PageHeaderTitle from '@/components/PageHeaderTitle.vue'
import PageBody from '@/components/PageBody.vue'
import Button from '@/components/Button.vue'
import List from '@/components/List.vue'
import ListItem from '@/components/ListItem.vue'
import StatusBubble from '@/components/StatusBubble.vue'
import NotificationBadge from '@/components/NotificationBadge.vue'
import MainLayout from '@/Layouts/MainLayout.vue'
import SettingsLayout from '@/components/layouts/SettingsLayout.vue'
import SettingsSegment from '@/components/SettingsSegment.vue'
import FormInput from '@/components/forms/FormInput.vue'
import FormFileInput from '@/components/forms/FormFileInput.vue'
import FormSelect from '@/components/forms/FormSelect.vue'
import Form from '@/components/Form.vue'
import FormActions from '@/components/FormActions.vue'
import {useNotification} from '@/hooks/notification'
import Tabs from './Tabs.vue'
export default {
metaInfo() {
return {
title: `${this.__('Settings')}`,
}
},
layout: MainLayout,
components: {
TopBar,
Container,
Content,
Page,
PageHeader,
PageHeaderTitle,
PageBody,
Button,
List,
ListItem,
StatusBubble,
NotificationBadge,
FormInput,
FormFileInput,
FormSelect,
SettingsLayout,
SettingsSegment,
Form,
FormActions,
Tabs,
},
data() {
return {
sending: false,
form: {
name: this.company_settings.name,
email: this.company_settings.email,
support_emails: this.company_settings.support_emails,
support: this.company_settings.support,
enable_api: this.company_settings.enable_api,
api_token: this.company_settings.api_token,
documentation: this.company_settings.documentation,
allow_registration: this.company_settings.allow_registration,
receive_email_on_server_creation: this.company_settings.receive_email_on_server_creation,
receive_email_on_site_creation: this.company_settings.receive_email_on_site_creation,
default_package: this.company_settings.default_package,
isolate_per_site_per_user: this.company_settings.isolate_per_site_per_user,
default_language: this.company_settings.default_language,
logo: null,
rotate_logs_after: this.company_settings.rotate_logs_after,
trialEnabled: false,
trial: this.company_settings.trial,
trial_package: this.company_settings.trial_package,
},
}
},
mounted() {
this.form.trialEnabled = !!this.company_settings.trial;
},
props: {
company_settings: Object,
packages: {},
languages: {}
},
methods: {
useNotification,
submit() {
var data = new FormData();
data.append('name', this.form.name || '')
data.append('email', this.form.email || '')
data.append('support_emails', this.form.support_emails || '')
data.append('support', this.form.support || false)
data.append('enable_api', this.form.enable_api || false)
data.append('api_token', this.form.api_token || '')
data.append('documentation', this.form.documentation || false)
data.append('allow_registration', this.form.allow_registration || false)
data.append('receive_email_on_server_creation', this.form.receive_email_on_server_creation || false)
data.append('receive_email_on_site_creation', this.form.receive_email_on_site_creation || false)
data.append('default_package', this.form.default_package || '')
data.append('isolate_per_site_per_user', this.form.isolate_per_site_per_user || false)
data.append('default_language', this.form.default_language || 'en')
data.append('logo', this.form.logo || '')
data.append('rotate_logs_after', this.form.rotate_logs_after || '')
data.append('trial', this.form.trial || '')
data.append('trial_package', this.form.trial_package || '')
data.append('_method', 'patch')
this.$inertia.post(this.route('admin.settings.update'), data, {
preserveScroll: true,
onStart: () => this.sending = true,
onFinish: () => this.sending = false,
onSuccess: () => {
if (Object.keys(this.$page.props.errors).length === 0) {
this.form.logo = null
}
},
})
},
removeLogo() {
this.$inertia.delete(this.route('admin.settings.remove-logo'), {
preserveScroll: true,
onStart: () => this.sending = true,
onFinish: () => this.sending = false,
});
}
}
}
</script>

View File

@@ -1,180 +0,0 @@
<template>
<Page>
<TopBar/>
<Content>
<Container>
<PageHeader>
<template #start>
<PageHeaderTitle>{{ __('Sites') }}</PageHeaderTitle>
</template>
</PageHeader>
<PageBody>
<SettingsLayout main-col-span="col-span-4">
<template #segments>
<SettingsSegment>
<template #title>{{ __('Sites') }}</template>
<template #form>
<form class="space-y-4 pb-5 mb-5 border-b border-low-emphasis">
<FormInput :label="__('Search')" :placeholder="__('Search on site domain, or the name/email of the owner of the site')" v-model="form.search" />
</form>
</template>
<template #content>
<div>
<Table caption="Site list overview">
<TableHead>
<TableRow>
<TableHeader>{{ __('Name') }}</TableHeader>
<TableHeader>{{ __('Server') }}</TableHeader>
<TableHeader>{{ __('Users') }}</TableHeader>
<TableHeader>{{ __('Date') }}</TableHeader>
<TableHeader></TableHeader>
</TableRow>
</TableHead>
<TableBody>
<TableRow v-for="site in sites.data" :key="site.id">
<TableData>
<div>{{ site.domain }}</div>
<div class="flex items-center space-x-2 text-sm text-medium-emphasis">
<div class="flex items-center space-x-2">
<span><icon-php /> </span>
<span>{{ site.php_version }}</span>
</div>
<div v-if="site.project === 'wordpress'">&centerdot;</div>
<div v-if="site.project === 'wordpress'">{{ __('WordPress installed') }}</div>
</div>
</TableData>
<TableData>
{{ site.server ? site.server.name : '-' }}
</TableData>
<TableData class="space-x-2">
<span v-if="!site.users || !site.users.length">-</span>
<inertia-link v-else class="text-primary" :href="route('admin.users.show', user.id)" :key="user.id" v-for="user in site.users">{{ user.name }}</inertia-link>
</TableData>
<TableData>
{{ site.created_at }}
</TableData>
<TableData>
<inertia-link :href="route('admin.services.sites.edit', site.id)" class="text-primary font-medium">
{{ __('Edit') }}
</inertia-link>
</TableData>
</TableRow>
</TableBody>
</Table>
</div>
<pagination :links="sites"/>
</template>
</SettingsSegment>
</template>
</SettingsLayout>
</PageBody>
</Container>
</Content>
</Page>
</template>
<script>
import TopBar from '../components/TopBar'
import Container from '@/components/Container'
import Content from '@/components/Content'
import Page from '@/components/Page'
import PageHeader from '@/components/PageHeader'
import PageHeaderTitle from '@/components/PageHeaderTitle'
import PageBody from '@/components/PageBody'
import Button from '@/components/Button'
import List from '@/components/List'
import ListItem from '@/components/ListItem'
import StatusBubble from '@/components/StatusBubble'
import NotificationBadge from '@/components/NotificationBadge'
import MainLayout from '@/Layouts/MainLayout'
import SettingsLayout from '@/components/layouts/SettingsLayout'
import SettingsSegment from '@/components/SettingsSegment'
import Pagination from '@/components/Pagination'
import {useNotification} from '@/hooks/notification'
import FormInput from '@/components/forms/FormInput'
import Table from '@/components/Table'
import TableHead from '@/components/TableHead'
import TableHeader from '@/components/TableHeader'
import TableRow from '@/components/TableRow'
import TableBody from '@/components/TableBody'
import TableData from '@/components/TableData'
import IconPhp from '@/components/icons/IconPhp'
import throttle from "lodash/throttle";
import pickBy from "lodash/pickBy";
export default {
layout: MainLayout,
metaInfo() {
return {
title: `${this.__('Sites')}`,
}
},
components: {
TopBar,
Container,
Content,
Page,
PageHeader,
PageHeaderTitle,
PageBody,
Button,
List,
ListItem,
StatusBubble,
NotificationBadge,
SettingsLayout,
SettingsSegment,
Pagination,
Table,
TableHead,
TableHeader,
TableRow,
TableBody,
TableData,
IconPhp,
FormInput
},
props: {
sites: Object,
filters: Object,
},
mounted() {
},
methods: {
useNotification,
},
data () {
return {
form: {
search: this.filters.search,
}
}
},
watch: {
form: {
handler: throttle(function() {
let query = pickBy(this.form)
this.$inertia.get(this.route('admin.sites.index', Object.keys(query).length ? query : { remember: 'forget' }), {},{
preserveScroll: true,
preserveState: true,
})
}, 500),
deep: true
},
},
}
</script>

View File

@@ -1,87 +0,0 @@
<template>
<Page>
<TopBar/>
<Content>
<Container>
<PageHeader>
<template #start>
<PageHeaderTitle>{{ __('Status') }}</PageHeaderTitle>
</template>
</PageHeader>
<PageBody>
<SettingsLayout>
<template #nav>
<Tabs />
</template>
<template #segments>
</template>
</SettingsLayout>
</PageBody>
</Container>
</Content>
</Page>
</template>
<script>
import TopBar from './components/TopBar.vue'
import Container from '@/components/Container.vue'
import Content from '@/components/Content.vue'
import Page from '@/components/Page.vue'
import PageHeader from '@/components/PageHeader.vue'
import PageHeaderTitle from '@/components/PageHeaderTitle.vue'
import PageBody from '@/components/PageBody.vue'
import Button from '@/components/Button.vue'
import List from '@/components/List.vue'
import ListItem from '@/components/ListItem.vue'
import StatusBubble from '@/components/StatusBubble.vue'
import NotificationBadge from '@/components/NotificationBadge.vue'
import MainLayout from '@/Layouts/MainLayout.vue'
import SettingsLayout from '@/components/layouts/SettingsLayout.vue'
import SettingsSegment from '@/components/SettingsSegment.vue'
import FormInput from '@/components/forms/FormInput.vue'
import Form from '@/components/Form.vue'
import FormActions from '@/components/FormActions.vue'
import {useNotification} from '@/hooks/notification'
import Tabs from './TabsStatus.vue'
export default {
metaInfo() {
return {
title: `${this.__('Status')}`,
}
},
layout: MainLayout,
components: {
TopBar,
Container,
Content,
Page,
PageHeader,
PageHeaderTitle,
PageBody,
Button,
List,
ListItem,
StatusBubble,
NotificationBadge,
FormInput,
SettingsLayout,
SettingsSegment,
Form,
FormActions,
Tabs
},
props: {
},
methods: {
useNotification,
}
}
</script>

View File

@@ -1,119 +0,0 @@
<template>
<Page>
<TopBar/>
<Content>
<Container>
<PageHeader>
<template #start>
<PageHeaderTitle>{{ __('Support') }}</PageHeaderTitle>
</template>
</PageHeader>
<PageBody>
<SettingsSegment>
<template #title>{{ __('Overview') }}</template>
<template #content>
<Table caption="Support list overview">
<TableHead>
<TableRow>
<TableHeader>{{ __('Name') }}</TableHeader>
<TableHeader>{{ __('Replies') }}</TableHeader>
<TableHeader></TableHeader>
</TableRow>
</TableHead>
<TableBody>
<TableRow v-for="ticket in tickets" :key="ticket.id">
<TableData>{{ ticket.title }}</TableData>
<TableData>{{ ticket.replies_count }}</TableData>
<TableData>
<inertia-link :href="route('admin.support.show', ticket.id)"
class="text-primary font-medium">
{{ __('View') }}
</inertia-link>
</TableData>
</TableRow>
</TableBody>
</Table>
</template>
</SettingsSegment>
</PageBody>
</Container>
</Content>
</Page>
</template>
<script>
import TopBar from '../components/TopBar'
import Container from '@/components/Container'
import Content from '@/components/Content'
import Page from '@/components/Page'
import PageHeader from '@/components/PageHeader'
import PageHeaderTitle from '@/components/PageHeaderTitle'
import PageBody from '@/components/PageBody'
import Button from '@/components/Button'
import List from '@/components/List'
import ListItem from '@/components/ListItem'
import StatusBubble from '@/components/StatusBubble'
import NotificationBadge from '@/components/NotificationBadge'
import MainLayout from '@/Layouts/MainLayout'
import SettingsLayout from '@/components/layouts/SettingsLayout'
import SettingsSegment from '@/components/SettingsSegment'
import Pagination from '@/components/Pagination'
import Tabs from './Tabs';
import Table from '@/components/Table'
import TableHead from '@/components/TableHead'
import TableHeader from '@/components/TableHeader'
import TableRow from '@/components/TableRow'
import TableBody from '@/components/TableBody'
import TableData from '@/components/TableData'
export default {
metaInfo() {
return {
title: `${this.__('Support')}`,
}
},
layout: MainLayout,
components: {
TopBar,
Container,
Content,
Page,
PageHeader,
PageHeaderTitle,
PageBody,
Button,
List,
ListItem,
StatusBubble,
NotificationBadge,
SettingsLayout,
SettingsSegment,
Pagination,
Tabs,
Table,
TableHead,
TableHeader,
TableRow,
TableBody,
TableData,
},
props: {
tickets: Array
},
data() {
return {
}
},
mounted() {
},
}
</script>

View File

@@ -1,150 +0,0 @@
<template>
<Page>
<TopBar/>
<Content>
<Container>
<PageHeader>
<template #start>
<PageHeaderTitle>{{ ticket.title }}</PageHeaderTitle>
</template>
</PageHeader>
<PageBody>
<div class="space-y-6">
<ul class="space-y-6">
<li class="p-6 rounded shadow bg-surface-3">
<div class="flex space-x-4">
<div>
<img :src="ticket.user.avatar" class="w-8 h-8 rounded-avatar bg-surface-2" />
</div>
<div>
<h3 class="font-semibold text-small text-body">{{ ticket.user.name }}</h3>
<p class="text-medium-emphasis">{{ ticket.content }}</p>
</div>
</div>
</li>
<li class="p-6 rounded shadow bg-surface-3" v-for="reply in replies">
<div class="flex space-x-4">
<div>
<img :src="reply.user.avatar" class="w-8 h-8 rounded-avatar bg-surface-2" />
</div>
<div>
<h3 class="font-semibold text-small text-body">{{ reply.user.name }}</h3>
<p class="text-medium-emphasis">
{{ reply.content }}
</p>
</div>
</div>
</li>
</ul>
<form class="space-y-4" @submit.prevent="reply">
<FormTextarea :label="__('Reply')" :errors="$page.props.errors.reply" v-model="form.content"/>
<FormActions>
<Button>{{ __('Save') }}</Button>
<Button type="button" @click="close" variant="secondary">{{ __('Close') }}</Button>
</FormActions>
</form>
</div>
</PageBody>
</Container>
</Content>
</Page>
</template>
<script>
import TopBar from '../components/TopBar'
import Container from '@/components/Container'
import Content from '@/components/Content'
import Page from '@/components/Page'
import PageHeader from '@/components/PageHeader'
import PageHeaderTitle from '@/components/PageHeaderTitle'
import PageBody from '@/components/PageBody'
import Button from '@/components/Button'
import List from '@/components/List'
import ListItem from '@/components/ListItem'
import StatusBubble from '@/components/StatusBubble'
import NotificationBadge from '@/components/NotificationBadge'
import MainLayout from '@/Layouts/MainLayout'
import IconBox from '@/components/icons/IconBox'
import IconGlobe from '@/components/icons/IconGlobe'
import IconStorage from '@/components/icons/IconStorage'
import Modal from '@/components/Modal'
import ModalContainer from '@/components/ModalContainer'
import FormInput from '@/components/forms/FormInput'
import FormTextarea from '@/components/forms/FormTextarea'
import FormActions from '@/components/FormActions'
export default {
metaInfo() {
return {
title: `${this.ticket.title}`,
}
},
layout: MainLayout,
components: {
TopBar,
Container,
Content,
Page,
PageHeader,
PageHeaderTitle,
PageBody,
Button,
List,
ListItem,
StatusBubble,
NotificationBadge,
IconBox,
IconGlobe,
IconStorage,
Modal,
ModalContainer,
FormInput,
FormTextarea,
FormActions
},
props: {
ticket: Object,
replies: Array,
},
data() {
return {
form: {
content: null
},
}
},
methods: {
reply() {
this.loading = true;
this.$inertia.post(this.route('admin.support.reply', this.ticket.id), this.form, {
onStart: () => this.sending = true,
onFinish: () => {
this.loading = false;
if (!Object.keys(this.$page.props.errors).length) {
this.form.content = null;
}
}
});
},
close() {
this.$inertia.post(this.route('admin.support.close', this.ticket.id), {
onStart: () => this.loading = true,
onFinish: () => this.loading = false
})
}
},
}
</script>

View File

@@ -1,27 +0,0 @@
<template>
<ul class="-ml-4 space-y-1">
<li v-for="item in items">
<inertia-link
class="flex items-center h-10 px-4 font-medium text-medium-emphasis"
:class="{'rounded shadow text-primary bg-surface-3': item.active}"
:href="item.to"
>{{ item.title }} {{ item.route }}</inertia-link
>
</li>
</ul>
</template>
<script>
export default {
data() {
return {
items: [
{
title: this.__('Overview'),
to: this.route('admin.support.index'),
active: this.route().current('admin.support.index')
},
],
}
},
}
</script>

View File

@@ -1,137 +0,0 @@
<template>
<Page>
<TopBar/>
<Content>
<Container>
<PageHeader>
<template #start>
<PageHeaderTitle>{{ __('System') }}</PageHeaderTitle>
</template>
</PageHeader>
<PageBody>
<SettingsLayout>
<template #nav>
<Tabs/>
</template>
<template #segments>
<SettingsSegment>
<template #title>{{ __('Overview') }}</template>
<template #subtitle>
Check your system's version here. If there's an update available you'll be able to
press the update button to update your system.
</template>
<template #content>
<p>{{ __('Current version') }}: {{ version.current }}</p>
<p>
{{ __('Remote version') }}: {{ version.remote }}
<button class="text-primary" type="button" v-on:click="refreshVersions">{{ 'Refresh' }}</button>
</p>
<p>Horizon worker status: <span v-if="horizonRunning" class="text-success">Active</span><span v-else class="text-danger">Inactive</span></p>
<div v-if="version.out_of_date && !updating" class="bg-primary text-on-primary px-4 py-3 rounded relative space-y-2" role="alert">
<strong class="font-bold">Update available</strong>
<p class="block">
An update is available for your system, please upgrade.
</p>
<a class="block font-bold underline" href="https://docs.ploi-core.io/digging-deeper/manual-update" target="_blank">Find out how to upgrade here</a>
</div>
</template>
</SettingsSegment>
<SettingsSegment>
<template #title>{{ __('Information') }}</template>
<template #content>
<ul class="list-disc list-inside">
<li><a target="_blank" class="text-primary" href="https://docs.ploi-core.io">Ploi Core Docs</a></li>
<li><a target="_blank" class="text-primary" href="https://ploi.io">Ploi Website</a></li>
<li><a target="_blank" class="text-primary" href="https://ploi.io/panel">Ploi Panel</a></li>
<li><a target="_blank" class="text-primary" href="https://github.com/ploi-deploy/ploi-core">Ploi Core Github Repository</a></li>
</ul>
</template>
</SettingsSegment>
</template>
</SettingsLayout>
</PageBody>
</Container>
</Content>
</Page>
</template>
<script>
import TopBar from './components/TopBar.vue'
import Container from '@/components/Container.vue'
import Content from '@/components/Content.vue'
import Page from '@/components/Page.vue'
import PageHeader from '@/components/PageHeader.vue'
import PageHeaderTitle from '@/components/PageHeaderTitle.vue'
import PageBody from '@/components/PageBody.vue'
import Button from '@/components/Button.vue'
import List from '@/components/List.vue'
import ListItem from '@/components/ListItem.vue'
import StatusBubble from '@/components/StatusBubble.vue'
import NotificationBadge from '@/components/NotificationBadge.vue'
import MainLayout from '@/Layouts/MainLayout.vue'
import SettingsLayout from '@/components/layouts/SettingsLayout.vue'
import SettingsSegment from '@/components/SettingsSegment.vue'
import FormInput from '@/components/forms/FormInput.vue'
import Form from '@/components/Form.vue'
import FormActions from '@/components/FormActions.vue'
import {useNotification} from '@/hooks/notification'
import Tabs from './Tabs.vue'
export default {
metaInfo() {
return {
title: `${this.__('System')}`,
}
},
layout: MainLayout,
components: {
TopBar,
Container,
Content,
Page,
PageHeader,
PageHeaderTitle,
PageBody,
Button,
List,
ListItem,
StatusBubble,
NotificationBadge,
FormInput,
SettingsLayout,
SettingsSegment,
Form,
FormActions,
Tabs
},
props: {
version: Object,
horizonRunning: Boolean
},
data() {
return {
sending: false,
updating: false,
}
},
methods: {
useNotification,
refreshVersions() {
this.$inertia.get(this.route('admin.system') + '?flush=true');
}
}
}
</script>

View File

@@ -1,52 +0,0 @@
<template>
<ul class="-ml-4 space-y-1">
<li v-for="item in items">
<inertia-link
class="flex items-center h-10 px-4 font-medium text-medium-emphasis"
:class="{'rounded shadow text-primary bg-surface-3': item.active}"
:href="item.to"
>{{ item.title }} {{ item.route }}</inertia-link
>
</li>
</ul>
</template>
<script>
export default {
data() {
return {
items: [
{
title: this.__('Overview'),
to: this.route('admin.dashboard'),
active: this.route().current('admin.dashboard')
},
{
title: this.__('Settings'),
to: this.route('admin.settings'),
active: this.route().current('admin.settings')
},
{
title: this.__('System'),
to: this.route('admin.system'),
active: this.route().current('admin.system')
},
{
title: this.__('Terms'),
to: this.route('admin.settings.terms'),
active: this.route().current('admin.settings.terms')
},
{
title: this.__('Alert messages'),
to: this.route('admin.alerts.index'),
active: this.route().current('admin.alerts.*')
},
{
title: this.__('Application logs'),
to: this.route('admin.application-logs'),
active: this.route().current('admin.application-logs')
},
],
}
},
}
</script>

View File

@@ -1,27 +0,0 @@
<template>
<ul class="-ml-4 space-y-1">
<li v-for="item in items">
<inertia-link
class="flex items-center h-10 px-4 font-medium text-medium-emphasis"
:class="{'rounded shadow text-primary bg-surface-3': item.active}"
:href="item.to"
>{{ item.title }} {{ item.route }}</inertia-link
>
</li>
</ul>
</template>
<script>
export default {
data() {
return {
items: [
{
title: this.__('Overview'),
to: this.route('admin.status.index'),
active: this.route().current('admin.status.index')
},
],
}
},
}
</script>

View File

@@ -1,179 +0,0 @@
<template>
<Page>
<TopBar/>
<Content>
<Container>
<PageHeader>
<template #start>
<PageHeaderTitle>{{ __('Terms') }}</PageHeaderTitle>
</template>
</PageHeader>
<PageBody>
<SettingsLayout>
<template #nav>
<Tabs/>
</template>
<template #segments>
<SettingsSegment>
<template #title>{{ __('Overview') }}</template>
<template #subtitle>
{{
__('Enter content for your terms of service and privacy policy here. You may use markdown.')
}}
</template>
<template #form>
<form class="space-y-4" @submit.prevent="submit">
<div>
<input id="terms_required" class="form-checkbox" type="checkbox"
v-model="form.terms_required">
<label for="terms_required" class="ml-2 text-sm">{{
__('Require users to accept terms of service on registration')
}}</label>
<p class="text-small mt-1 text-medium-emphasis">
{{
__('This will require newly registered users to accept the terms of service.')
}}
</p>
</div>
<Button type="button" size="sm" @click="getTemplate('terms')">
Load Terms of Service template
</Button>
<FormCustom label="Content Terms Of Service">
<vue-simplemde v-model="form.terms" ref="terms_of_service"/>
</FormCustom>
<FormCustom label="Content Privacy Policy">
<vue-simplemde v-model="form.privacy"/>
</FormCustom>
<FormActions>
<Button>{{ __('Save') }}</Button>
</FormActions>
</form>
</template>
</SettingsSegment>
</template>
</SettingsLayout>
</PageBody>
</Container>
</Content>
</Page>
</template>
<script>
import TopBar from './components/TopBar'
import Container from '@/components/Container'
import Content from '@/components/Content'
import Page from '@/components/Page'
import PageHeader from '@/components/PageHeader'
import PageHeaderTitle from '@/components/PageHeaderTitle'
import PageBody from '@/components/PageBody'
import Button from '@/components/Button'
import List from '@/components/List'
import ListItem from '@/components/ListItem'
import StatusBubble from '@/components/StatusBubble'
import NotificationBadge from '@/components/NotificationBadge'
import MainLayout from '@/Layouts/MainLayout'
import SettingsLayout from '@/components/layouts/SettingsLayout'
import SettingsSegment from '@/components/SettingsSegment'
import FormInput from '@/components/forms/FormInput'
import FormFileInput from '@/components/forms/FormFileInput'
import FormSelect from '@/components/forms/FormSelect'
import Form from '@/components/Form'
import FormActions from '@/components/FormActions'
import FormCustom from '@/components/forms/FormCustom'
import {useNotification} from '@/hooks/notification'
import Tabs from './Tabs'
import VueSimplemde from 'vue-simplemde'
export default {
metaInfo() {
return {
title: `${this.__('Terms')}`,
}
},
layout: MainLayout,
components: {
TopBar,
Container,
Content,
Page,
PageHeader,
PageHeaderTitle,
PageBody,
Button,
List,
ListItem,
StatusBubble,
NotificationBadge,
FormInput,
FormFileInput,
FormSelect,
SettingsLayout,
SettingsSegment,
Form,
FormActions,
Tabs,
VueSimplemde,
FormCustom
},
data() {
return {
sending: false,
form: this.$inertia.form({
terms: this.terms_settings.terms,
terms_required: this.terms_settings.terms_required,
privacy: this.terms_settings.privacy,
})
}
},
props: {
terms_settings: Object,
},
methods: {
useNotification,
submit() {
this.form.patch(this.route('admin.settings.terms.update'));
},
getTemplate(type) {
window.axios.get(this.route('admin.settings.terms.template'))
.then(response => {
this.$refs.terms_of_service.simplemde.value(response.data.content);
this.form.terms = response.data.content;
useNotification({
variant: 'success',
title: this.__(`Terms`),
message: 'Template has been loaded in, do not forget to save.',
})
})
}
}
}
</script>
<style>
@import '../../../node_modules/simplemde/dist/simplemde.min.css';
.editor-toolbar.fullscreen {
z-index: 50;
}
.CodeMirror-fullscreen {
z-index: 50;
}
.editor-preview-side {
z-index: 50;
}
</style>

View File

@@ -1,152 +0,0 @@
<template>
<Page>
<TopBar/>
<Content>
<Container>
<PageHeader>
<template #start>
<PageHeaderTitle>{{ __('Create user') }}</PageHeaderTitle>
</template>
</PageHeader>
<PageBody>
<SettingsLayout>
<template #nav>
<Tabs />
</template>
<template #segments>
<SettingsSegment>
<template #title>{{ __('Create') }}</template>
<template #form>
<form class="space-y-4" @submit.prevent="submit">
<FormInput :label="__('Name')" :errors="$page.props.errors.name" v-model="form.name" />
<FormInput label="Email address" :errors="$page.props.errors.email" type="email" v-model="form.email" />
<FormTextarea label="Notes" :errors="$page.props.errors.notes" v-model="form.notes" />
<FormSelect :label="__('Role')" v-model="form.role">
<option value="user">{{ __('User') }}</option>
<option value="admin">{{ __('Administrator') }}</option>
</FormSelect>
<FormSelect :label="__('Package')" v-model="form.package">
<option value="" v-text="__('None')"></option>
<option v-for="(name, id) in packages" :value="id" v-text="name"></option>
</FormSelect>
<FormSelect :label="__('Language')" v-model="form.language">
<option v-for="language in languages" :value="language" v-text="language"></option>
</FormSelect>
<FormTextarea :label="__('Blocked')" :errors="$page.props.errors.blocked" rows="2" v-model="form.blocked" />
<div>
<input id="isolate_per_site_per_user" class="form-checkbox" type="checkbox"
v-model="form.requires_password_for_ftp">
<label for="isolate_per_site_per_user" class="ml-2 text-sm">{{ __('Require password to show FTP password') }}</label>
<p class="text-small mt-1 text-medium-emphasis">
{{ __('Disabling this will allow this user to get the FTP password right away.') }}
</p>
</div>
<FormActions>
<Button>{{ __('Save') }}</Button>
</FormActions>
</form>
</template>
</SettingsSegment>
</template>
</SettingsLayout>
</PageBody>
</Container>
</Content>
</Page>
</template>
<script>
import TopBar from '../components/TopBar'
import Container from '@/components/Container'
import Content from '@/components/Content'
import Page from '@/components/Page'
import PageHeader from '@/components/PageHeader'
import PageHeaderTitle from '@/components/PageHeaderTitle'
import PageBody from '@/components/PageBody'
import Button from '@/components/Button'
import List from '@/components/List'
import ListItem from '@/components/ListItem'
import StatusBubble from '@/components/StatusBubble'
import NotificationBadge from '@/components/NotificationBadge'
import MainLayout from '@/Layouts/MainLayout'
import SettingsLayout from '@/components/layouts/SettingsLayout'
import SettingsSegment from '@/components/SettingsSegment'
import FormInput from '@/components/forms/FormInput'
import FormTextarea from '@/components/forms/FormTextarea'
import FormSelect from '@/components/forms/FormSelect'
import Form from '@/components/Form'
import FormActions from '@/components/FormActions'
import Tabs from './Tabs'
export default {
metaInfo() {
return {
title: `${this.__('Create user')}`,
}
},
layout: MainLayout,
components: {
TopBar,
Container,
Content,
Page,
PageHeader,
PageHeaderTitle,
PageBody,
Button,
List,
ListItem,
StatusBubble,
NotificationBadge,
FormInput,
FormSelect,
SettingsLayout,
SettingsSegment,
Form,
FormActions,
Tabs,
FormTextarea
},
props: {
packages: Object,
languages: Array,
defaultPackage: {},
defaultLanguage: {}
},
data() {
return {
sending: false,
form: {
name: null,
email: null,
password: null,
role: 'user',
package: this.defaultPackage ?? null,
notes: null,
language: this.defaultLanguage ?? 'en',
blocked: null,
requires_password_for_ftp: true,
},
}
},
methods: {
submit() {
this.$inertia.post(this.route('admin.users.store'), this.form, {
onStart: () => this.sending = true,
onFinish: () => this.sending = false
})
}
}
}
</script>

View File

@@ -1,180 +0,0 @@
<template>
<Page>
<TopBar/>
<Content>
<Container>
<PageHeader>
<template #start>
<PageHeaderTitle>{{ __('Edit user') }}</PageHeaderTitle>
</template>
</PageHeader>
<PageBody>
<SettingsLayout>
<template #nav>
<Tabs/>
</template>
<template #segments>
<SettingsSegment>
<template #title>{{ __('Edit') }}</template>
<template #form>
<form class="space-y-4" @submit.prevent="submit">
<FormInput :label="__('Name')" :errors="$page.props.errors.name"
v-model="form.name"/>
<FormInput :label="__('Email')" :errors="$page.props.errors.email" type="email"
v-model="form.email"/>
<FormTextarea :label="__('Notes')" :errors="$page.props.errors.notes"
v-model="form.notes"/>
<FormSelect :label="__('Role')" v-model="form.role">
<option value="user">User</option>
<option value="admin">Administrator</option>
</FormSelect>
<FormSelect :label="__('Package')" v-model="form.package">
<option value="" v-text="__('None')"></option>
<option v-for="(name, id) in packages" :value="id" v-text="name"></option>
</FormSelect>
<FormSelect :label="__('Language')" v-model="form.language">
<option v-for="language in languages" :value="language"
v-text="language"></option>
</FormSelect>
<FormTextarea :label="__('Blocked')" :errors="$page.props.errors.blocked"
rows="2" v-model="form.blocked"/>
<div>
<input id="isolate_per_site_per_user" class="form-checkbox" type="checkbox"
v-model="form.requires_password_for_ftp">
<label for="isolate_per_site_per_user" class="ml-2 text-sm">{{
__('Require password to show FTP password')
}}</label>
<p class="text-small mt-1 text-medium-emphasis">
{{
__('Disabling this will allow this user to get the FTP password right away.')
}}
</p>
</div>
<FormActions>
<Button>{{ __('Save') }}</Button>
<Button variant="danger" type="button" @click="confirmDelete">
{{ __('Delete') }}
</Button>
</FormActions>
</form>
</template>
</SettingsSegment>
</template>
</SettingsLayout>
</PageBody>
</Container>
</Content>
</Page>
</template>
<script>
import TopBar from '../components/TopBar'
import Container from '@/components/Container'
import Content from '@/components/Content'
import Page from '@/components/Page'
import PageHeader from '@/components/PageHeader'
import PageHeaderTitle from '@/components/PageHeaderTitle'
import PageBody from '@/components/PageBody'
import Button from '@/components/Button'
import List from '@/components/List'
import ListItem from '@/components/ListItem'
import StatusBubble from '@/components/StatusBubble'
import NotificationBadge from '@/components/NotificationBadge'
import MainLayout from '@/Layouts/MainLayout'
import SettingsLayout from '@/components/layouts/SettingsLayout'
import SettingsSegment from '@/components/SettingsSegment'
import FormInput from '@/components/forms/FormInput'
import FormTextarea from '@/components/forms/FormTextarea'
import FormSelect from '@/components/forms/FormSelect'
import Form from '@/components/Form'
import FormActions from '@/components/FormActions'
import {useConfirm} from '@/hooks/confirm'
import Tabs from './Tabs'
export default {
metaInfo() {
return {
title: `${this.__('Edit user')}`,
}
},
layout: MainLayout,
components: {
TopBar,
Container,
Content,
Page,
PageHeader,
PageHeaderTitle,
PageBody,
Button,
List,
ListItem,
StatusBubble,
NotificationBadge,
FormInput,
FormSelect,
SettingsLayout,
SettingsSegment,
Form,
FormActions,
Tabs,
FormTextarea
},
data() {
return {
sending: false,
form: {
name: this.user.name,
email: this.user.email,
role: this.user.role,
package: this.user.package_id,
notes: this.user.notes,
language: this.user.language,
blocked: this.user.blocked,
requires_password_for_ftp: this.user.requires_password_for_ftp,
},
}
},
props: {
user: Object,
packages: Object,
languages: Array,
},
methods: {
submit() {
this.$inertia.patch(this.route('admin.users.update', this.user.id), this.form, {
onStart: () => this.sending = true,
onFinish: () => this.sending = false
})
},
confirmDelete() {
useConfirm({
title: this.__('Are you sure?'),
message: `Are you sure you want to delete this user? Everything associated with this user will be detached.`,
onConfirm: () => this.delete(),
})
},
delete() {
this.sending = true
this.$inertia.delete(this.route('admin.users.destroy', this.user.id), {
onFinish: () => {
this.sending = false;
}
})
}
}
}
</script>

View File

@@ -1,177 +0,0 @@
<template>
<Page>
<TopBar/>
<Content>
<Container>
<PageHeader>
<template #start>
<PageHeaderTitle>{{ __('Users') }}</PageHeaderTitle>
</template>
</PageHeader>
<PageBody>
<SettingsLayout>
<template #nav>
<Tabs />
</template>
<template #segments>
<SettingsSegment>
<template #title>{{ __('Overview') }}</template>
<template #form>
<form class="space-y-4 pb-5 mb-5 border-b border-low-emphasis">
<FormInput :label="__('Search')" v-model="form.search" />
</form>
</template>
<template #content>
<div>
<Table caption="User list overview">
<TableHead>
<TableRow>
<TableHeader>{{ __('Name') }}</TableHeader>
<TableHeader>{{ __('User name') }}</TableHeader>
<TableHeader>{{ __('Role') }}</TableHeader>
<TableHeader>{{ __('Package') }}</TableHeader>
<TableHeader></TableHeader>
</TableRow>
</TableHead>
<TableBody>
<TableRow v-for="user in users.data" :key="user.id">
<TableData>
<inertia-link :href="route('admin.users.show', user.id)">
<span class="text-primary">{{ user.name }}</span>
<p class="text-medium-emphasis">{{ user.email }}</p>
<p class="text-medium-emphasis">{{ user.sites_count }} {{ __choice('site|sites', user.sites_count) }} &centerdot; {{ user.servers_count }} {{ __choice('server|servers', user.servers_count) }}</p>
</inertia-link>
</TableData>
<TableData>{{ user.user_name }}</TableData>
<TableData>{{ user.role }}</TableData>
<TableData>{{ user.package ? user.package.name : '-' }}</TableData>
<TableData>
<inertia-link :href="route('admin.users.edit', user.id)"
class="text-primary font-medium">
{{ __('Edit') }}
</inertia-link>
</TableData>
</TableRow>
</TableBody>
</Table>
</div>
<pagination :links="users"/>
</template>
</SettingsSegment>
</template>
</SettingsLayout>
</PageBody>
</Container>
</Content>
</Page>
</template>
<script>
import TopBar from '../components/TopBar'
import Container from '@/components/Container'
import Content from '@/components/Content'
import Page from '@/components/Page'
import PageHeader from '@/components/PageHeader'
import PageHeaderTitle from '@/components/PageHeaderTitle'
import PageBody from '@/components/PageBody'
import Button from '@/components/Button'
import List from '@/components/List'
import ListItem from '@/components/ListItem'
import StatusBubble from '@/components/StatusBubble'
import NotificationBadge from '@/components/NotificationBadge'
import MainLayout from '@/Layouts/MainLayout'
import SettingsLayout from '@/components/layouts/SettingsLayout'
import SettingsSegment from '@/components/SettingsSegment'
import Pagination from '@/components/Pagination'
import {useNotification} from '@/hooks/notification'
import FormInput from '@/components/forms/FormInput'
import Tabs from './Tabs';
import Table from '@/components/Table'
import TableHead from '@/components/TableHead'
import TableHeader from '@/components/TableHeader'
import TableRow from '@/components/TableRow'
import TableBody from '@/components/TableBody'
import TableData from '@/components/TableData'
import pickBy from 'lodash/pickBy'
import throttle from 'lodash/throttle'
export default {
metaInfo() {
return {
title: `${this.__('Users')}`,
}
},
layout: MainLayout,
components: {
TopBar,
Container,
Content,
Page,
PageHeader,
PageHeaderTitle,
PageBody,
Button,
List,
ListItem,
StatusBubble,
NotificationBadge,
SettingsLayout,
SettingsSegment,
Pagination,
Tabs,
Table,
TableHead,
TableHeader,
TableRow,
TableBody,
TableData,
FormInput,
},
props: {
users: Object,
filters: Object,
},
data() {
return {
form:{
search: this.filters.search,
}
}
},
watch: {
form: {
handler: throttle(function() {
let query = pickBy(this.form)
this.$inertia.get(this.route('admin.users.index', Object.keys(query).length ? query : { remember: 'forget' }),{}, {
preserveState: true,
})
}, 500),
deep: true
},
},
mounted() {
if (this.$page.props.flash.success) {
useNotification({
variant: 'success',
title: `Users`,
message: this.$page.props.flash.success,
})
}
},
methods: {
useNotification,
},
}
</script>

View File

@@ -1,226 +0,0 @@
<template>
<Page>
<TopBar/>
<Content>
<Container>
<PageHeader>
<template #start>
<PageHeaderTitle>Show user</PageHeaderTitle>
</template>
</PageHeader>
<PageBody>
<SettingsLayout>
<template #nav>
<Tabs />
</template>
<template #segments>
<SettingsSegment>
<template #title>{{ user.name }}</template>
<template #content>
<div>
<Table caption="Database list overview">
<TableBody>
<TableRow>
<TableHeader>{{ __('Name') }}</TableHeader>
<TableData :border="false">
{{ user.name }}
</TableData>
</TableRow>
<TableRow>
<TableHeader>{{ __('Username') }}</TableHeader>
<TableData :border="false">
{{ user.user_name }}
</TableData>
</TableRow>
<TableRow>
<TableHeader>{{ __('E-mail address') }}</TableHeader>
<TableData :border="false">
{{ user.email }}
</TableData>
</TableRow>
<TableRow>
<TableHeader>{{ __('Customer payment ID') }}</TableHeader>
<TableData :border="false">
{{ user.stripe_id || '-' }}
</TableData>
</TableRow>
<TableRow>
<TableHeader>{{ __('Notes') }}</TableHeader>
<TableData :border="false">
{{ user.notes || '-' }}
</TableData>
</TableRow>
</TableBody>
</Table>
</div>
</template>
</SettingsSegment>
<SettingsSegment>
<template #title>{{ __('Sites') }}</template>
<template #content>
<div>
<Table caption="Site list overview">
<TableHead>
<TableRow>
<TableHeader>{{ __('Name') }}</TableHeader>
<TableHeader>{{ __('Server') }}</TableHeader>
<TableHeader></TableHeader>
</TableRow>
</TableHead>
<TableBody>
<TableRow v-for="site in sites.data" :key="site.id">
<TableData>
{{ site.domain }}
</TableData>
<TableData>
{{ site.server ? site.server.name : '-' }}
</TableData>
<TableData>
<inertia-link :href="route('admin.services.sites.edit', site.id)" class="text-primary font-medium">
{{ __('Edit') }}
</inertia-link>
</TableData>
</TableRow>
</TableBody>
</Table>
</div>
<pagination :links="sites" :preserve-scroll="true"/>
</template>
</SettingsSegment>
<SettingsSegment>
<template #title>{{ __('Servers') }}</template>
<template #content>
<div>
<Table caption="Server list overview">
<TableHead>
<TableRow>
<TableHeader>{{ __('Name') }}</TableHeader>
<TableHeader>{{ __('Usage') }}</TableHeader>
<TableHeader></TableHeader>
</TableRow>
</TableHead>
<TableBody>
<TableRow v-for="server in servers.data" :key="server.id">
<TableData>
{{ server.name }}
<p class="text-medium-emphasis">{{ server.ip }}</p>
<Button class="text-small text-primary" is="a" size="sm" target="_blank" :href="`https://ploi.io/panel/servers/${server.ploi_id}`">
{{ __('View in ploi.io') }}
</Button>
</TableData>
<TableData>{{ server.sites_count }}/{{ server.maximum_sites }}</TableData>
<TableData>
<inertia-link :href="route('admin.services.servers.edit', server.id)" class="text-primary font-medium">
{{ __('Edit') }}
</inertia-link>
</TableData>
</TableRow>
</TableBody>
</Table>
</div>
<pagination :links="servers" :preserve-scroll="true"/>
</template>
</SettingsSegment>
</template>
</SettingsLayout>
</PageBody>
</Container>
</Content>
</Page>
</template>
<script>
import TopBar from '../components/TopBar'
import Container from '@/components/Container'
import Content from '@/components/Content'
import Page from '@/components/Page'
import PageHeader from '@/components/PageHeader'
import PageHeaderTitle from '@/components/PageHeaderTitle'
import PageBody from '@/components/PageBody'
import Button from '@/components/Button'
import List from '@/components/List'
import ListItem from '@/components/ListItem'
import StatusBubble from '@/components/StatusBubble'
import NotificationBadge from '@/components/NotificationBadge'
import MainLayout from '@/Layouts/MainLayout'
import SettingsLayout from '@/components/layouts/SettingsLayout'
import SettingsSegment from '@/components/SettingsSegment'
import FormInput from '@/components/forms/FormInput'
import FormTextarea from '@/components/forms/FormTextarea'
import FormSelect from '@/components/forms/FormSelect'
import Form from '@/components/Form'
import FormActions from '@/components/FormActions'
import Table from '@/components/Table'
import TableHead from '@/components/TableHead'
import TableHeader from '@/components/TableHeader'
import TableRow from '@/components/TableRow'
import TableBody from '@/components/TableBody'
import TableData from '@/components/TableData'
import Tabs from './Tabs'
import Pagination from '@/components/Pagination'
export default {
metaInfo() {
return {
title: `${this.__(this.user.name)}`,
}
},
layout: MainLayout,
components: {
TopBar,
Container,
Content,
Page,
PageHeader,
PageHeaderTitle,
PageBody,
Button,
List,
ListItem,
StatusBubble,
NotificationBadge,
FormInput,
FormSelect,
SettingsLayout,
SettingsSegment,
Form,
FormActions,
Tabs,
FormTextarea,
Table,
TableHead,
TableHeader,
TableRow,
TableBody,
TableData,
Pagination
},
data() {
return {
}
},
props: {
user: Object,
sites: Object,
servers: Object,
},
methods: {
}
}
</script>

View File

@@ -1,32 +0,0 @@
<template>
<ul class="-ml-4 space-y-1">
<li v-for="item in items">
<inertia-link
class="flex items-center h-10 px-4 font-medium text-medium-emphasis"
:class="{'rounded shadow text-primary bg-surface-3': item.active}"
:href="item.to"
>{{ item.title }} {{ item.route }}</inertia-link
>
</li>
</ul>
</template>
<script>
export default {
data() {
return {
items: [
{
title: this.__('Overview'),
to: this.route('admin.users.index'),
active: this.route().current('admin.users.index')
},
{
title: this.__('Create'),
to: this.route('admin.users.create'),
active: this.route().current('admin.users.create')
}
],
}
},
}
</script>

View File

@@ -1,93 +0,0 @@
<template>
<TopBar>
<template #breadcrumbs>
<Breadcrumbs :items="breadcrumbs" />
</template>
<template #tab-bar>
<TopBarTabBarContainer>
<TabBar :items="tabBars" />
</TopBarTabBarContainer>
</template>
</TopBar>
</template>
<script>
import TopBar from '@/components/TopBar.vue'
import Breadcrumbs from '@/components/Breadcrumbs.vue'
import TopBarTabBarContainer from '@/components/TopBarTabBarContainer.vue'
import TabBar from "@/components/TabBar.vue";
export default {
components: {
TopBar,
Breadcrumbs,
TabBar,
TopBarTabBarContainer,
},
data() {
return {
tabBars: [
{
title: 'Dashboard',
to: this.route('admin.dashboard'),
active: (
this.route().current('admin.dashboard') ||
this.route().current('admin.settings') ||
this.route().current('admin.system')
)
},
{
title: this.__('Users'),
to: this.route('admin.users.index'),
active: this.route().current('admin.users.*')
},
{
title: this.__('Packages'),
to: this.route('admin.packages.index'),
active: this.route().current('admin.packages.*')
},
{
title: this.__('Sites'),
to: this.route('admin.sites.index'),
active: this.route().current('admin.sites.*')
},
{
title: this.__('Servers'),
to: this.route('admin.servers.index'),
active: this.route().current('admin.servers.*')
},
this.$page.props.settings.support ? {
title: `${this.__('Support')} (${this.$page.props.openTickets})`,
to: this.route('admin.support.index'),
active: this.route().current('admin.support.*')
} : null,
this.$page.props.settings.documentation ? {
title: this.__('Documentation'),
to: this.route('admin.documentation.index'),
active: this.route().current('admin.documentation.*')
} : null,
{
title: 'Services',
to: this.route('admin.services.index'),
active: this.route().current('admin.services.*')
},
// {
// title: 'Status',
// to: this.route('admin.status.index'),
// active: this.route().current('admin.status.*')
// }
],
breadcrumbs: [
{
title: this.$page.props.settings.name,
to: '/',
},
{
title: this.__('Administration'),
to: this.route('admin.dashboard'),
}
],
}
},
}
</script>

View File

@@ -1,106 +0,0 @@
<?php
use Illuminate\Support\Facades\Route;
Route::get('/', 'DashboardController')->name('dashboard');
Route::group(['prefix' => 'settings'], function () {
Route::get('/', 'SettingController@index')->name('settings');
Route::patch('/', 'SettingController@update')->name('settings.update');
Route::get('terms', 'SettingController@terms')->name('settings.terms');
Route::get('terms/template', 'SettingController@termsTemplate')->name('settings.terms.template');
Route::patch('terms', 'SettingController@updateTerms')->name('settings.terms.update');
Route::delete('remove-logo', 'SettingController@removeLogo')->name('settings.remove-logo');
});
Route::get('system', 'SystemController@index')->name('system');
Route::post('system/update', 'SystemController@update')->name('system.update');
Route::get('status', 'StatusController@index')->name('status.index');
Route::get('application-logs', 'ApplicationLogController@index')->name('application-logs');
Route::group(['prefix' => 'support', 'as' => 'support.'], function () {
Route::get('/', 'SupportController@index')->name('index');
Route::get('{ticket}', 'SupportController@show')->name('show');
Route::post('{ticket}/reply', 'SupportController@reply')->name('reply');
Route::post('{ticket}/close', 'SupportController@close')->name('close');
});
Route::group(['prefix' => 'alerts', 'as' => 'alerts.'], function () {
Route::get('/', 'AlertController@index')->name('index');
Route::get('create', 'AlertController@create')->name('create');
Route::post('/', 'AlertController@store')->name('store');
Route::get('{alert}/edit', 'AlertController@edit')->name('edit');
Route::patch('{alert}', 'AlertController@update')->name('update');
Route::delete('{alert}', 'AlertController@destroy')->name('delete');
});
Route::group(['prefix' => 'documentation', 'as' => 'documentation.'], function () {
Route::get('/', 'DocumentationController@index')->name('index');
Route::get('create', 'DocumentationController@create')->name('create');
Route::post('/', 'DocumentationController@store')->name('store');
Route::get('{category}/edit', 'DocumentationController@edit')->name('edit');
Route::patch('{category}', 'DocumentationController@update')->name('update');
Route::group(['prefix' => 'articles', 'as' => 'articles.'], function () {
Route::get('/', 'DocumentationArticleController@index')->name('index');
Route::get('create', 'DocumentationArticleController@create')->name('create');
Route::post('/', 'DocumentationArticleController@store')->name('store');
Route::get('{article}/edit', 'DocumentationArticleController@edit')->name('edit');
Route::patch('{article}', 'DocumentationArticleController@update')->name('update');
});
});
Route::group(['prefix' => 'sites', 'as' => 'sites.'], function () {
Route::get('/', 'SiteController@index')->name('index');
});
Route::group(['prefix' => 'servers', 'as' => 'servers.'], function () {
Route::get('/', 'ServerController@index')->name('index');
});
Route::group(['prefix' => 'services', 'as' => 'services.'], function () {
Route::get('/', 'ServiceController@index')->name('index');
// Server syncing
Route::group(['prefix' => 'servers', 'as' => 'servers.'], function () {
Route::get('/', 'SynchronizeServerController@index')->name('index');
Route::post('/', 'SynchronizeServerController@synchronizeServer')->name('sync');
Route::post('all', 'SynchronizeServerController@synchronizeAll')->name('sync.all');
Route::get('{server}/edit', 'ServerController@edit')->name('edit');
Route::post('{server}/attach', 'ServerController@attach')->name('attach');
Route::patch('{server}', 'ServerController@update')->name('update');
Route::delete('{server}', 'ServerController@destroy')->name('delete');
Route::delete('{server}/detach/{user}', 'ServerController@detach')->name('detach');
});
// Provider syncing
Route::group(['prefix' => 'providers', 'as' => 'providers.'], function () {
Route::get('/', 'SynchronizeProviderController@index')->name('index');
Route::post('{provider}', 'SynchronizeProviderController@synchronize')->name('sync');
Route::get('{provider}/edit', 'ProviderController@edit')->name('edit');
Route::patch('{provider}', 'ProviderController@update')->name('update');
Route::delete('{provider}', 'ProviderController@destroy')->name('delete');
});
// Site syncing
Route::group(['prefix' => 'sites', 'as' => 'sites.'], function () {
Route::get('/', 'SynchronizeSiteController@index')->name('index');
Route::post('/', 'SynchronizeSiteController@synchronizeSite')->name('sync');
Route::post('all', 'SynchronizeSiteController@synchronizeAll')->name('sync.all');
Route::get('{site}/edit', 'SiteController@edit')->name('edit');
Route::post('{site}/attach', 'SiteController@attach')->name('attach');
Route::patch('{site}', 'SiteController@update')->name('update');
Route::delete('{site}', 'SiteController@destroy')->name('delete');
Route::delete('{site}/detach/{user}', 'SiteController@detach')->name('detach');
});
});
Route::resource('packages', 'PackageController');
Route::resource('providers', 'ProviderController');
Route::resource('plans', 'ProviderPlanController');
Route::resource('regions', 'ProviderRegionController');
Route::resource('users', 'UserController');