Redesign (#124)
- redesign the dashboard - add search bar - Mobile-friendly design
58
app/Http/Controllers/SearchController.php
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use App\Models\Server;
|
||||||
|
use App\Models\Site;
|
||||||
|
use Illuminate\Http\JsonResponse;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
class SearchController extends Controller
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @throws ValidationException
|
||||||
|
*/
|
||||||
|
public function search(Request $request): JsonResponse
|
||||||
|
{
|
||||||
|
$this->validate($request, [
|
||||||
|
'q' => 'required',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$servers = Server::query()
|
||||||
|
->where(function ($query) use ($request) {
|
||||||
|
$query->where('name', 'like', '%'.$request->input('q').'%')
|
||||||
|
->orWhere('ip', 'like', '%'.$request->input('q').'%');
|
||||||
|
})
|
||||||
|
->get();
|
||||||
|
|
||||||
|
$sites = Site::query()
|
||||||
|
->where('domain', 'like', '%'.$request->input('q').'%')
|
||||||
|
->get();
|
||||||
|
|
||||||
|
$result = [];
|
||||||
|
|
||||||
|
/** @var Server $server */
|
||||||
|
foreach ($servers as $server) {
|
||||||
|
$result[] = [
|
||||||
|
'type' => 'server',
|
||||||
|
'url' => route('servers.show', ['server' => $server]),
|
||||||
|
'text' => $server->name,
|
||||||
|
'project' => $server->project->name,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @var Site $site */
|
||||||
|
foreach ($sites as $site) {
|
||||||
|
$result[] = [
|
||||||
|
'type' => 'site',
|
||||||
|
'url' => route('servers.sites.show', ['server' => $site->server, 'site' => $site]),
|
||||||
|
'text' => $site->domain,
|
||||||
|
'project' => $site->server->project->name,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'results' => $result,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
@ -3,6 +3,7 @@
|
|||||||
namespace App\Http;
|
namespace App\Http;
|
||||||
|
|
||||||
use App\Http\Middleware\HandleSSHErrors;
|
use App\Http\Middleware\HandleSSHErrors;
|
||||||
|
use App\Http\Middleware\SelectCurrentProject;
|
||||||
use App\Http\Middleware\ServerIsReadyMiddleware;
|
use App\Http\Middleware\ServerIsReadyMiddleware;
|
||||||
use Illuminate\Foundation\Http\Kernel as HttpKernel;
|
use Illuminate\Foundation\Http\Kernel as HttpKernel;
|
||||||
|
|
||||||
@ -66,5 +67,6 @@ class Kernel extends HttpKernel
|
|||||||
'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
|
'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
|
||||||
'server-is-ready' => ServerIsReadyMiddleware::class,
|
'server-is-ready' => ServerIsReadyMiddleware::class,
|
||||||
'handle-ssh-errors' => HandleSSHErrors::class,
|
'handle-ssh-errors' => HandleSSHErrors::class,
|
||||||
|
'select-current-project' => SelectCurrentProject::class,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
32
app/Http/Middleware/SelectCurrentProject.php
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Middleware;
|
||||||
|
|
||||||
|
use App\Models\User;
|
||||||
|
use Closure;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
|
||||||
|
class SelectCurrentProject
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Handle an incoming request.
|
||||||
|
*
|
||||||
|
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
|
||||||
|
*/
|
||||||
|
public function handle(Request $request, Closure $next): Response
|
||||||
|
{
|
||||||
|
/** @var Server $server */
|
||||||
|
$server = $request->route('server');
|
||||||
|
|
||||||
|
/** @var User $user */
|
||||||
|
$user = $request->user();
|
||||||
|
|
||||||
|
if ($server->project_id != $user->current_project_id) {
|
||||||
|
$user->current_project_id = $server->project_id;
|
||||||
|
$user->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $next($request);
|
||||||
|
}
|
||||||
|
}
|
175
package-lock.json
generated
@ -9,7 +9,7 @@
|
|||||||
"@tailwindcss/typography": "^0.5.9",
|
"@tailwindcss/typography": "^0.5.9",
|
||||||
"alpinejs": "^3.4.2",
|
"alpinejs": "^3.4.2",
|
||||||
"autoprefixer": "^10.4.2",
|
"autoprefixer": "^10.4.2",
|
||||||
"axios": "^1.6.0",
|
"flowbite": "^2.3.0",
|
||||||
"htmx.org": "^1.9.10",
|
"htmx.org": "^1.9.10",
|
||||||
"laravel-echo": "^1.15.0",
|
"laravel-echo": "^1.15.0",
|
||||||
"laravel-vite-plugin": "^0.7.2",
|
"laravel-vite-plugin": "^0.7.2",
|
||||||
@ -564,12 +564,6 @@
|
|||||||
"integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==",
|
"integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/asynckit": {
|
|
||||||
"version": "0.4.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
|
|
||||||
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"node_modules/autoprefixer": {
|
"node_modules/autoprefixer": {
|
||||||
"version": "10.4.14",
|
"version": "10.4.14",
|
||||||
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.14.tgz",
|
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.14.tgz",
|
||||||
@ -603,17 +597,6 @@
|
|||||||
"postcss": "^8.1.0"
|
"postcss": "^8.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/axios": {
|
|
||||||
"version": "1.6.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.6.0.tgz",
|
|
||||||
"integrity": "sha512-EZ1DYihju9pwVB+jg67ogm+Tmqc6JmhamRN6I4Zt8DfZu5lbcQGw3ozH9lFejSJgs/ibaef3A9PMXPLeefFGJg==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"follow-redirects": "^1.15.0",
|
|
||||||
"form-data": "^4.0.0",
|
|
||||||
"proxy-from-env": "^1.1.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/balanced-match": {
|
"node_modules/balanced-match": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||||
@ -753,18 +736,6 @@
|
|||||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/combined-stream": {
|
|
||||||
"version": "1.0.8",
|
|
||||||
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
|
||||||
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"delayed-stream": "~1.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/commander": {
|
"node_modules/commander": {
|
||||||
"version": "4.1.1",
|
"version": "4.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz",
|
||||||
@ -792,15 +763,6 @@
|
|||||||
"node": ">=4"
|
"node": ">=4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/delayed-stream": {
|
|
||||||
"version": "1.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
|
||||||
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
|
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
|
||||||
"node": ">=0.4.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/didyoumean": {
|
"node_modules/didyoumean": {
|
||||||
"version": "1.2.2",
|
"version": "1.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz",
|
||||||
@ -926,38 +888,14 @@
|
|||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/follow-redirects": {
|
"node_modules/flowbite": {
|
||||||
"version": "1.15.5",
|
"version": "2.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.5.tgz",
|
"resolved": "https://registry.npmjs.org/flowbite/-/flowbite-2.3.0.tgz",
|
||||||
"integrity": "sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==",
|
"integrity": "sha512-pm3JRo8OIJHGfFYWgaGpPv8E+UdWy0Z3gEAGufw+G/1dusaU/P1zoBLiQpf2/+bYAi+GBQtPVG86KYlV0W+AFQ==",
|
||||||
"dev": true,
|
|
||||||
"funding": [
|
|
||||||
{
|
|
||||||
"type": "individual",
|
|
||||||
"url": "https://github.com/sponsors/RubenVerborgh"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=4.0"
|
|
||||||
},
|
|
||||||
"peerDependenciesMeta": {
|
|
||||||
"debug": {
|
|
||||||
"optional": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/form-data": {
|
|
||||||
"version": "4.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
|
|
||||||
"integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
|
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"asynckit": "^0.4.0",
|
"@popperjs/core": "^2.9.3",
|
||||||
"combined-stream": "^1.0.8",
|
"mini-svg-data-uri": "^1.4.3"
|
||||||
"mime-types": "^2.1.12"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 6"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/fraction.js": {
|
"node_modules/fraction.js": {
|
||||||
@ -1220,27 +1158,6 @@
|
|||||||
"node": ">=8.6"
|
"node": ">=8.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/mime-db": {
|
|
||||||
"version": "1.52.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
|
||||||
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
|
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/mime-types": {
|
|
||||||
"version": "2.1.35",
|
|
||||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
|
|
||||||
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"mime-db": "1.52.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/mini-svg-data-uri": {
|
"node_modules/mini-svg-data-uri": {
|
||||||
"version": "1.4.4",
|
"version": "1.4.4",
|
||||||
"resolved": "https://registry.npmjs.org/mini-svg-data-uri/-/mini-svg-data-uri-1.4.4.tgz",
|
"resolved": "https://registry.npmjs.org/mini-svg-data-uri/-/mini-svg-data-uri-1.4.4.tgz",
|
||||||
@ -1620,12 +1537,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/proxy-from-env": {
|
|
||||||
"version": "1.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
|
|
||||||
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"node_modules/pusher-js": {
|
"node_modules/pusher-js": {
|
||||||
"version": "4.4.0",
|
"version": "4.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/pusher-js/-/pusher-js-4.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/pusher-js/-/pusher-js-4.4.0.tgz",
|
||||||
@ -2392,12 +2303,6 @@
|
|||||||
"integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==",
|
"integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"asynckit": {
|
|
||||||
"version": "0.4.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
|
|
||||||
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"autoprefixer": {
|
"autoprefixer": {
|
||||||
"version": "10.4.14",
|
"version": "10.4.14",
|
||||||
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.14.tgz",
|
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.14.tgz",
|
||||||
@ -2412,17 +2317,6 @@
|
|||||||
"postcss-value-parser": "^4.2.0"
|
"postcss-value-parser": "^4.2.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"axios": {
|
|
||||||
"version": "1.6.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.6.0.tgz",
|
|
||||||
"integrity": "sha512-EZ1DYihju9pwVB+jg67ogm+Tmqc6JmhamRN6I4Zt8DfZu5lbcQGw3ozH9lFejSJgs/ibaef3A9PMXPLeefFGJg==",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"follow-redirects": "^1.15.0",
|
|
||||||
"form-data": "^4.0.0",
|
|
||||||
"proxy-from-env": "^1.1.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"balanced-match": {
|
"balanced-match": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||||
@ -2511,15 +2405,6 @@
|
|||||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"combined-stream": {
|
|
||||||
"version": "1.0.8",
|
|
||||||
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
|
||||||
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"delayed-stream": "~1.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"commander": {
|
"commander": {
|
||||||
"version": "4.1.1",
|
"version": "4.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz",
|
||||||
@ -2538,12 +2423,6 @@
|
|||||||
"integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
|
"integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"delayed-stream": {
|
|
||||||
"version": "1.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
|
||||||
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"didyoumean": {
|
"didyoumean": {
|
||||||
"version": "1.2.2",
|
"version": "1.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz",
|
||||||
@ -2649,21 +2528,14 @@
|
|||||||
"to-regex-range": "^5.0.1"
|
"to-regex-range": "^5.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"follow-redirects": {
|
"flowbite": {
|
||||||
"version": "1.15.5",
|
"version": "2.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.5.tgz",
|
"resolved": "https://registry.npmjs.org/flowbite/-/flowbite-2.3.0.tgz",
|
||||||
"integrity": "sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==",
|
"integrity": "sha512-pm3JRo8OIJHGfFYWgaGpPv8E+UdWy0Z3gEAGufw+G/1dusaU/P1zoBLiQpf2/+bYAi+GBQtPVG86KYlV0W+AFQ==",
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"form-data": {
|
|
||||||
"version": "4.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
|
|
||||||
"integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
|
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"asynckit": "^0.4.0",
|
"@popperjs/core": "^2.9.3",
|
||||||
"combined-stream": "^1.0.8",
|
"mini-svg-data-uri": "^1.4.3"
|
||||||
"mime-types": "^2.1.12"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"fraction.js": {
|
"fraction.js": {
|
||||||
@ -2864,21 +2736,6 @@
|
|||||||
"picomatch": "^2.3.1"
|
"picomatch": "^2.3.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"mime-db": {
|
|
||||||
"version": "1.52.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
|
||||||
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"mime-types": {
|
|
||||||
"version": "2.1.35",
|
|
||||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
|
|
||||||
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"mime-db": "1.52.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"mini-svg-data-uri": {
|
"mini-svg-data-uri": {
|
||||||
"version": "1.4.4",
|
"version": "1.4.4",
|
||||||
"resolved": "https://registry.npmjs.org/mini-svg-data-uri/-/mini-svg-data-uri-1.4.4.tgz",
|
"resolved": "https://registry.npmjs.org/mini-svg-data-uri/-/mini-svg-data-uri-1.4.4.tgz",
|
||||||
@ -3072,12 +2929,6 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {}
|
"requires": {}
|
||||||
},
|
},
|
||||||
"proxy-from-env": {
|
|
||||||
"version": "1.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
|
|
||||||
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"pusher-js": {
|
"pusher-js": {
|
||||||
"version": "4.4.0",
|
"version": "4.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/pusher-js/-/pusher-js-4.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/pusher-js/-/pusher-js-4.4.0.tgz",
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
"@tailwindcss/typography": "^0.5.9",
|
"@tailwindcss/typography": "^0.5.9",
|
||||||
"alpinejs": "^3.4.2",
|
"alpinejs": "^3.4.2",
|
||||||
"autoprefixer": "^10.4.2",
|
"autoprefixer": "^10.4.2",
|
||||||
"axios": "^1.6.0",
|
"flowbite": "^2.3.0",
|
||||||
"htmx.org": "^1.9.10",
|
"htmx.org": "^1.9.10",
|
||||||
"laravel-echo": "^1.15.0",
|
"laravel-echo": "^1.15.0",
|
||||||
"laravel-vite-plugin": "^0.7.2",
|
"laravel-vite-plugin": "^0.7.2",
|
||||||
|
1
public/build/assets/app-986a0fb7.css
Normal file
21
public/build/assets/app-e823d2ab.js
Normal file
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"resources/css/app.css": {
|
"resources/css/app.css": {
|
||||||
"file": "assets/app-e2e337dc.css",
|
"file": "assets/app-986a0fb7.css",
|
||||||
"isEntry": true,
|
"isEntry": true,
|
||||||
"src": "resources/css/app.css"
|
"src": "resources/css/app.css"
|
||||||
},
|
},
|
||||||
@ -12,7 +12,7 @@
|
|||||||
"css": [
|
"css": [
|
||||||
"assets/app-a1ae07b3.css"
|
"assets/app-a1ae07b3.css"
|
||||||
],
|
],
|
||||||
"file": "assets/app-f7ac9558.js",
|
"file": "assets/app-e823d2ab.js",
|
||||||
"isEntry": true,
|
"isEntry": true,
|
||||||
"src": "resources/js/app.js"
|
"src": "resources/js/app.js"
|
||||||
}
|
}
|
||||||
|
@ -6,3 +6,7 @@
|
|||||||
[x-cloak] {
|
[x-cloak] {
|
||||||
display: none !important;
|
display: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
@apply text-gray-700 dark:text-gray-300;
|
||||||
|
}
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import 'flowbite';
|
||||||
|
|
||||||
import Alpine from 'alpinejs';
|
import Alpine from 'alpinejs';
|
||||||
window.Alpine = Alpine;
|
window.Alpine = Alpine;
|
||||||
Alpine.start();
|
Alpine.start();
|
||||||
|
15
resources/js/bootstrap.js
vendored
@ -1,15 +0,0 @@
|
|||||||
import axios from 'axios';
|
|
||||||
|
|
||||||
window.axios = axios;
|
|
||||||
|
|
||||||
window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
|
|
||||||
|
|
||||||
import toastr from 'toastr';
|
|
||||||
|
|
||||||
window.toastr = toastr;
|
|
||||||
|
|
||||||
window.toastr.options = {
|
|
||||||
"debug": false,
|
|
||||||
"positionClass": "toast-bottom-right",
|
|
||||||
"preventDuplicates": true,
|
|
||||||
}
|
|
@ -11,14 +11,17 @@
|
|||||||
</x-card-header>
|
</x-card-header>
|
||||||
<x-live id="live-deployments">
|
<x-live id="live-deployments">
|
||||||
<x-table>
|
<x-table>
|
||||||
<tr>
|
<x-thead>
|
||||||
|
<x-tr>
|
||||||
<x-th>{{ __("Commit") }}</x-th>
|
<x-th>{{ __("Commit") }}</x-th>
|
||||||
<x-th>{{ __("Date") }}</x-th>
|
<x-th>{{ __("Date") }}</x-th>
|
||||||
<x-th>{{ __("Status") }}</x-th>
|
<x-th>{{ __("Status") }}</x-th>
|
||||||
<x-th></x-th>
|
<x-th></x-th>
|
||||||
</tr>
|
</x-tr>
|
||||||
|
</x-thead>
|
||||||
|
<x-tbody>
|
||||||
@foreach ($deployments as $deployment)
|
@foreach ($deployments as $deployment)
|
||||||
<tr>
|
<x-tr>
|
||||||
<x-td class="truncate">
|
<x-td class="truncate">
|
||||||
<a
|
<a
|
||||||
href="{{ $deployment->commit_data["url"] }}"
|
href="{{ $deployment->commit_data["url"] }}"
|
||||||
@ -48,8 +51,9 @@ class="block max-w-[500px] truncate font-semibold text-primary-600"
|
|||||||
<x-heroicon name="o-eye" class="h-5 w-5" />
|
<x-heroicon name="o-eye" class="h-5 w-5" />
|
||||||
</x-icon-button>
|
</x-icon-button>
|
||||||
</x-td>
|
</x-td>
|
||||||
</tr>
|
</x-tr>
|
||||||
@endforeach
|
@endforeach
|
||||||
|
</x-tbody>
|
||||||
</x-table>
|
</x-table>
|
||||||
</x-live>
|
</x-live>
|
||||||
<div class="mt-5">
|
<div class="mt-5">
|
||||||
|
@ -5,12 +5,12 @@
|
|||||||
{{ __("Here you can manage your application") }}
|
{{ __("Here you can manage your application") }}
|
||||||
</x-slot>
|
</x-slot>
|
||||||
<x-slot name="aside">
|
<x-slot name="aside">
|
||||||
<div class="flex items-center">
|
<div class="flex flex-col items-end lg:flex-row lg:items-center">
|
||||||
<div class="mr-2">
|
<div class="mb-2 lg:mb-0 lg:mr-2">
|
||||||
@include("application.deploy")
|
@include("application.deploy")
|
||||||
</div>
|
</div>
|
||||||
@if ($site->source_control_id)
|
@if ($site->source_control_id)
|
||||||
<div class="mr-2">
|
<div class="mb-2 lg:mb-0 lg:mr-2">
|
||||||
@include("application.auto-deployment")
|
@include("application.auto-deployment")
|
||||||
</div>
|
</div>
|
||||||
@endif
|
@endif
|
||||||
|
@ -1,3 +1,10 @@
|
|||||||
<div {!! $attributes->merge(["class" => "py-12 max-w-7xl mx-auto px-6"]) !!}>
|
@php
|
||||||
|
$class = "mx-auto px-4 sm:px-6 lg:px-8";
|
||||||
|
if (! str($attributes->get("class"))->contains("max-w-")) {
|
||||||
|
$class .= " max-w-7xl";
|
||||||
|
}
|
||||||
|
@endphp
|
||||||
|
|
||||||
|
<div {!! $attributes->merge(["class" => $class]) !!}>
|
||||||
{{ $slot }}
|
{{ $slot }}
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<button
|
<button
|
||||||
{{ $attributes->merge(["type" => "submit", "class" => "inline-flex w-max items-center justify-center rounded-md border border-transparent bg-red-600 px-4 py-1 h-9 font-semibold capitalize text-white transition hover:bg-red-500 focus:border-red-700 focus:outline-none focus:ring focus:ring-red-200 active:bg-red-600 disabled:opacity-25"]) }}
|
{{ $attributes->merge(["type" => "submit", "class" => "inline-flex w-max min-w-max items-center justify-center rounded-md border border-transparent bg-red-600 px-4 py-1 h-9 font-semibold capitalize text-white transition hover:bg-red-500 focus:border-red-700 focus:outline-none focus:ring focus:ring-red-200 active:bg-red-600 disabled:opacity-25"]) }}
|
||||||
>
|
>
|
||||||
{{ $slot }}
|
{{ $slot }}
|
||||||
</button>
|
</button>
|
||||||
|
@ -1,5 +1,11 @@
|
|||||||
<a
|
@props(["active" => false])
|
||||||
{{ $attributes->merge(["class" => "block w-full px-4 py-2 text-left text-sm leading-5 text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700 focus:outline-none focus:bg-gray-100 dark:focus:bg-gray-700 transition duration-150 ease-in-out flex items-center justify-start"]) }}
|
|
||||||
>
|
@php
|
||||||
|
$class = $active
|
||||||
|
? "block flex w-full items-center justify-start px-4 py-2 text-left text-sm leading-5 text-primary-500 transition duration-150 ease-in-out hover:bg-gray-100 focus:bg-gray-100 focus:outline-none dark:hover:bg-gray-800/50 dark:focus:bg-gray-700"
|
||||||
|
: "block flex w-full items-center justify-start px-4 py-2 text-left text-sm leading-5 text-gray-700 transition duration-150 ease-in-out hover:bg-gray-100 focus:bg-gray-100 focus:outline-none dark:text-gray-300 dark:hover:bg-gray-800/50 dark:focus:bg-gray-700";
|
||||||
|
@endphp
|
||||||
|
|
||||||
|
<a {{ $attributes->merge(["class" => $class]) }}>
|
||||||
{{ $slot }}
|
{{ $slot }}
|
||||||
</a>
|
</a>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
@props(["align" => "right", "width" => "48", "contentClasses" => "bg-white py-1 dark:bg-gray-800"])
|
@props(["open" => false, "align" => "right", "width" => "48", "contentClasses" => "list-none divide-y divide-gray-100 rounded-md border border-gray-100 bg-white py-1 text-base shadow dark:divide-gray-600 dark:border-gray-600 dark:bg-gray-700"])
|
||||||
|
|
||||||
@php
|
@php
|
||||||
switch ($align) {
|
switch ($align) {
|
||||||
@ -24,7 +24,7 @@
|
|||||||
}
|
}
|
||||||
@endphp
|
@endphp
|
||||||
|
|
||||||
<div class="relative" x-data="{ open: false }" @click.outside="open = false" @close.stop="open = false">
|
<div class="relative" x-data="{ open: @js($open) }" @click.outside="open = false" @close.stop="open = false">
|
||||||
<div @click="open = ! open">
|
<div @click="open = ! open">
|
||||||
{{ $trigger }}
|
{{ $trigger }}
|
||||||
</div>
|
</div>
|
||||||
|
@ -0,0 +1,10 @@
|
|||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
stroke-width="1.5"
|
||||||
|
stroke="currentColor"
|
||||||
|
{{ $attributes }}
|
||||||
|
>
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" d="M3.75 6.75h16.5M3.75 12H12m-8.25 5.25h16.5" />
|
||||||
|
</svg>
|
After Width: | Height: | Size: 271 B |
14
resources/views/components/heroicons/o-bell.blade.php
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
stroke-width="1.5"
|
||||||
|
stroke="currentColor"
|
||||||
|
{{ $attributes }}
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
d="M14.857 17.082a23.848 23.848 0 0 0 5.454-1.31A8.967 8.967 0 0 1 18 9.75V9A6 6 0 0 0 6 9v.75a8.967 8.967 0 0 1-2.312 6.022c1.733.64 3.56 1.085 5.455 1.31m5.714 0a24.255 24.255 0 0 1-5.714 0m5.714 0a3 3 0 1 1-5.714 0"
|
||||||
|
/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 471 B |
@ -0,0 +1,10 @@
|
|||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
stroke-width="1.5"
|
||||||
|
stroke="currentColor"
|
||||||
|
{{ $attributes }}
|
||||||
|
>
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" d="m19.5 8.25-7.5 7.5-7.5-7.5" />
|
||||||
|
</svg>
|
After Width: | Height: | Size: 255 B |
14
resources/views/components/heroicons/o-inbox-stack.blade.php
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
stroke-width="1.5"
|
||||||
|
stroke="currentColor"
|
||||||
|
{{ $attributes }}
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
d="m7.875 14.25 1.214 1.942a2.25 2.25 0 0 0 1.908 1.058h2.006c.776 0 1.497-.4 1.908-1.058l1.214-1.942M2.41 9h4.636a2.25 2.25 0 0 1 1.872 1.002l.164.246a2.25 2.25 0 0 0 1.872 1.002h2.092a2.25 2.25 0 0 0 1.872-1.002l.164-.246A2.25 2.25 0 0 1 16.954 9h4.636M2.41 9a2.25 2.25 0 0 0-.16.832V12a2.25 2.25 0 0 0 2.25 2.25h15A2.25 2.25 0 0 0 21.75 12V9.832c0-.287-.055-.57-.16-.832M2.41 9a2.25 2.25 0 0 1 .382-.632l3.285-3.832a2.25 2.25 0 0 1 1.708-.786h8.43c.657 0 1.281.287 1.709.786l3.284 3.832c.163.19.291.404.382.632M4.5 20.25h15A2.25 2.25 0 0 0 21.75 18v-2.625c0-.621-.504-1.125-1.125-1.125H3.375c-.621 0-1.125.504-1.125 1.125V18a2.25 2.25 0 0 0 2.25 2.25Z"
|
||||||
|
/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 908 B |
@ -0,0 +1,14 @@
|
|||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
stroke-width="1.5"
|
||||||
|
stroke="currentColor"
|
||||||
|
{{ $attributes }}
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
d="m21 21-5.197-5.197m0 0A7.5 7.5 0 1 0 5.196 5.196a7.5 7.5 0 0 0 10.607 10.607Z"
|
||||||
|
/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 334 B |
@ -0,0 +1,14 @@
|
|||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
stroke-width="1.5"
|
||||||
|
stroke="currentColor"
|
||||||
|
{{ $attributes }}
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
d="M5.25 14.25h13.5m-13.5 0a3 3 0 0 1-3-3m3 3a3 3 0 1 0 0 6h13.5a3 3 0 1 0 0-6m-16.5-3a3 3 0 0 1 3-3h13.5a3 3 0 0 1 3 3m-19.5 0a4.5 4.5 0 0 1 .9-2.7L5.737 5.1a3.375 3.375 0 0 1 2.7-1.35h7.126c1.062 0 2.062.5 2.7 1.35l2.587 3.45a4.5 4.5 0 0 1 .9 2.7m0 0a3 3 0 0 1-3 3m0 3h.008v.008h-.008v-.008Zm0-6h.008v.008h-.008v-.008Zm-3 6h.008v.008h-.008v-.008Zm0-6h.008v.008h-.008v-.008Z"
|
||||||
|
/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 629 B |
14
resources/views/components/heroicons/o-user-circle.blade.php
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
stroke-width="1.5"
|
||||||
|
stroke="currentColor"
|
||||||
|
{{ $attributes }}
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
d="M17.982 18.725A7.488 7.488 0 0 0 12 15.75a7.488 7.488 0 0 0-5.982 2.975m11.963 0a9 9 0 1 0-11.963 0m11.963 0A8.966 8.966 0 0 1 12 21a8.966 8.966 0 0 1-5.982-2.275M15 9.75a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z"
|
||||||
|
/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 456 B |
1
resources/views/components/hr.blade.php
Normal file
@ -0,0 +1 @@
|
|||||||
|
<div class="my-3 border-b border-gray-200 dark:border-gray-700"></div>
|
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
@php
|
@php
|
||||||
$class =
|
$class =
|
||||||
"inline-flex h-9 w-max items-center justify-center rounded-md border border-transparent bg-primary-600 px-4 py-1 font-semibold text-white outline-0 transition hover:bg-primary-700 focus:border-primary-300 focus:border-primary-700 focus:ring focus:ring-primary-200 focus:ring-opacity-50 active:bg-primary-700 disabled:opacity-25 dark:focus:border-primary-700 dark:focus:ring-primary-700 dark:focus:ring-opacity-40";
|
"inline-flex h-9 w-max min-w-max items-center justify-center rounded-md border border-transparent bg-primary-600 px-4 py-1 font-semibold text-white outline-0 transition hover:bg-primary-700 focus:border-primary-300 focus:border-primary-700 focus:ring focus:ring-primary-200 focus:ring-opacity-50 active:bg-primary-700 disabled:opacity-25 dark:focus:border-primary-700 dark:focus:ring-primary-700 dark:focus:ring-opacity-40";
|
||||||
@endphp
|
@endphp
|
||||||
|
|
||||||
@if (isset($href))
|
@if (isset($href))
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
@php
|
@php
|
||||||
$class =
|
$class =
|
||||||
"inline-flex h-9 items-center rounded-md border border-gray-300 bg-white px-4 py-1 font-semibold text-gray-700 shadow-sm transition duration-150 ease-in-out hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-gray-500 focus:ring-offset-2 disabled:opacity-25 dark:border-gray-500 dark:bg-gray-800 dark:text-gray-300 dark:hover:bg-gray-700 dark:focus:ring-offset-gray-800";
|
"inline-flex h-9 min-w-max items-center rounded-md border border-gray-300 bg-white px-4 py-1 font-semibold text-gray-700 shadow-sm transition duration-150 ease-in-out hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-gray-500 focus:ring-offset-2 disabled:opacity-25 dark:border-gray-500 dark:bg-gray-800 dark:text-gray-300 dark:hover:bg-gray-700 dark:focus:ring-offset-gray-800";
|
||||||
@endphp
|
@endphp
|
||||||
|
|
||||||
@if (isset($href))
|
@if (isset($href))
|
||||||
|
@ -5,8 +5,8 @@
|
|||||||
@php
|
@php
|
||||||
$classes =
|
$classes =
|
||||||
$active ?? false
|
$active ?? false
|
||||||
? "text-md flex h-10 items-center rounded-md bg-gray-900 px-4 py-3 font-semibold text-primary-500 transition transition-all duration-100 duration-150 ease-in-out"
|
? "group flex items-center rounded-md bg-primary-700 p-2 text-white"
|
||||||
: "text-md flex h-10 items-center rounded-md px-4 py-3 font-semibold text-gray-500 transition transition-all duration-100 duration-150 ease-in-out hover:bg-gray-900";
|
: "group flex items-center rounded-md p-2 text-gray-900 hover:bg-gray-100 dark:text-white dark:hover:bg-gray-700";
|
||||||
@endphp
|
@endphp
|
||||||
|
|
||||||
<a {{ $attributes->merge(["class" => $classes]) }}>
|
<a {{ $attributes->merge(["class" => $classes]) }}>
|
||||||
|
@ -5,8 +5,8 @@
|
|||||||
@php
|
@php
|
||||||
$classes =
|
$classes =
|
||||||
$active ?? false
|
$active ?? false
|
||||||
? "flex h-10 items-center justify-start rounded-lg bg-primary-50 px-3 py-2 font-semibold text-primary-500 hover:bg-gray-100 dark:bg-primary-500 dark:bg-opacity-20 dark:hover:bg-gray-800"
|
? "flex h-10 items-center justify-start rounded-lg bg-primary-50 px-3 py-2 font-semibold text-primary-500 dark:bg-primary-500 dark:bg-opacity-20"
|
||||||
: "flex h-10 items-center justify-start rounded-lg px-3 py-2 font-semibold text-gray-800 hover:bg-gray-100 dark:text-gray-300 dark:hover:bg-gray-800";
|
: "flex h-10 items-center justify-start rounded-lg px-3 py-2 font-semibold text-gray-800 hover:bg-gray-100 dark:text-gray-300 dark:hover:bg-gray-700";
|
||||||
@endphp
|
@endphp
|
||||||
|
|
||||||
<a {{ $attributes->merge(["class" => $classes]) }}>
|
<a {{ $attributes->merge(["class" => $classes]) }}>
|
@ -1,5 +1,5 @@
|
|||||||
<div
|
<div
|
||||||
{!! $attributes->merge(["class" => "inline-block min-w-full overflow-x-auto rounded-md bg-white align-middle border border-gray-200 dark:border-gray-700 dark:bg-gray-800"]) !!}
|
{!! $attributes->merge(["class" => "inline-block min-w-full max-w-full overflow-x-auto rounded-md bg-white align-middle border border-gray-200 dark:border-gray-700 dark:bg-gray-800"]) !!}
|
||||||
>
|
>
|
||||||
<table class="min-w-full">
|
<table class="min-w-full">
|
||||||
{{ $slot }}
|
{{ $slot }}
|
||||||
|
3
resources/views/components/tbody.blade.php
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<tbody {!! $attributes->merge(["class" => ""]) !!}>
|
||||||
|
{{ $slot }}
|
||||||
|
</tbody>
|
5
resources/views/components/thead.blade.php
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<thead
|
||||||
|
{!! $attributes->merge(["class" => "text-xs text-gray-700 uppercase bg-gray-50 dark:bg-gray-700 dark:text-gray-400 border-b border-gray-200 dark:border-gray-600"]) !!}
|
||||||
|
>
|
||||||
|
{{ $slot }}
|
||||||
|
</thead>
|
5
resources/views/components/tr.blade.php
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<tr
|
||||||
|
{!! $attributes->merge(["class" => "dark:text-white bg-white border-b last:border-b-0 dark:bg-gray-800 dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-800/70"]) !!}
|
||||||
|
>
|
||||||
|
{{ $slot }}
|
||||||
|
</tr>
|
@ -21,7 +21,7 @@ class="ml-1"
|
|||||||
<x-live id="live-backup-files">
|
<x-live id="live-backup-files">
|
||||||
@if (count($files) > 0)
|
@if (count($files) > 0)
|
||||||
<x-table class="mt-5">
|
<x-table class="mt-5">
|
||||||
<tr>
|
<x-tr>
|
||||||
<x-th>{{ __("Name") }}</x-th>
|
<x-th>{{ __("Name") }}</x-th>
|
||||||
<x-th>{{ __("Created") }}</x-th>
|
<x-th>{{ __("Created") }}</x-th>
|
||||||
{{-- <x-th>{{ __("Size") }}</x-th> --}}
|
{{-- <x-th>{{ __("Size") }}</x-th> --}}
|
||||||
@ -29,9 +29,9 @@ class="ml-1"
|
|||||||
<x-th>{{ __("Restored") }}</x-th>
|
<x-th>{{ __("Restored") }}</x-th>
|
||||||
<x-th>{{ __("Restored To") }}</x-th>
|
<x-th>{{ __("Restored To") }}</x-th>
|
||||||
<x-th></x-th>
|
<x-th></x-th>
|
||||||
</tr>
|
</x-tr>
|
||||||
@foreach ($files as $file)
|
@foreach ($files as $file)
|
||||||
<tr>
|
<x-tr>
|
||||||
<x-td>{{ $file->name }}</x-td>
|
<x-td>{{ $file->name }}</x-td>
|
||||||
<x-td>
|
<x-td>
|
||||||
<x-datetime :value="$file->created_at" />
|
<x-datetime :value="$file->created_at" />
|
||||||
@ -71,7 +71,7 @@ class="ml-1"
|
|||||||
<x-heroicon name="o-trash" class="h-5 w-5" />
|
<x-heroicon name="o-trash" class="h-5 w-5" />
|
||||||
</x-icon-button>
|
</x-icon-button>
|
||||||
</x-td>
|
</x-td>
|
||||||
</tr>
|
</x-tr>
|
||||||
@endforeach
|
@endforeach
|
||||||
</x-table>
|
</x-table>
|
||||||
<div class="mt-5">
|
<div class="mt-5">
|
||||||
|
@ -13,15 +13,15 @@
|
|||||||
<x-live id="live-backups">
|
<x-live id="live-backups">
|
||||||
@if (count($backups) > 0)
|
@if (count($backups) > 0)
|
||||||
<x-table>
|
<x-table>
|
||||||
<tr>
|
<x-tr>
|
||||||
<x-th>{{ __("Database") }}</x-th>
|
<x-th>{{ __("Database") }}</x-th>
|
||||||
<x-th>{{ __("Created") }}</x-th>
|
<x-th>{{ __("Created") }}</x-th>
|
||||||
<x-th>{{ __("Storage") }}</x-th>
|
<x-th>{{ __("Storage") }}</x-th>
|
||||||
<x-th>{{ __("Status") }}</x-th>
|
<x-th>{{ __("Status") }}</x-th>
|
||||||
<x-th></x-th>
|
<x-th></x-th>
|
||||||
</tr>
|
</x-tr>
|
||||||
@foreach ($backups as $backup)
|
@foreach ($backups as $backup)
|
||||||
<tr>
|
<x-tr>
|
||||||
<x-td>{{ $backup->database->name }}</x-td>
|
<x-td>{{ $backup->database->name }}</x-td>
|
||||||
<x-td>
|
<x-td>
|
||||||
<x-datetime :value="$backup->created_at" />
|
<x-datetime :value="$backup->created_at" />
|
||||||
@ -44,7 +44,7 @@
|
|||||||
<x-heroicon name="o-trash" class="h-5 w-5" />
|
<x-heroicon name="o-trash" class="h-5 w-5" />
|
||||||
</x-icon-button>
|
</x-icon-button>
|
||||||
</x-td>
|
</x-td>
|
||||||
</tr>
|
</x-tr>
|
||||||
@endforeach
|
@endforeach
|
||||||
</x-table>
|
</x-table>
|
||||||
@else
|
@else
|
||||||
|
@ -16,14 +16,14 @@
|
|||||||
<x-live id="live-databases">
|
<x-live id="live-databases">
|
||||||
@if (count($databases) > 0)
|
@if (count($databases) > 0)
|
||||||
<x-table>
|
<x-table>
|
||||||
<tr>
|
<x-tr>
|
||||||
<x-th>{{ __("Name") }}</x-th>
|
<x-th>{{ __("Name") }}</x-th>
|
||||||
<x-th>{{ __("Created") }}</x-th>
|
<x-th>{{ __("Created") }}</x-th>
|
||||||
<x-th>{{ __("Status") }}</x-th>
|
<x-th>{{ __("Status") }}</x-th>
|
||||||
<x-th></x-th>
|
<x-th></x-th>
|
||||||
</tr>
|
</x-tr>
|
||||||
@foreach ($databases as $database)
|
@foreach ($databases as $database)
|
||||||
<tr>
|
<x-tr>
|
||||||
<x-td>{{ $database->name }}</x-td>
|
<x-td>{{ $database->name }}</x-td>
|
||||||
<x-td>
|
<x-td>
|
||||||
<x-datetime :value="$database->created_at" />
|
<x-datetime :value="$database->created_at" />
|
||||||
@ -40,7 +40,7 @@
|
|||||||
<x-heroicon name="o-trash" class="h-5 w-5" />
|
<x-heroicon name="o-trash" class="h-5 w-5" />
|
||||||
</x-icon-button>
|
</x-icon-button>
|
||||||
</x-td>
|
</x-td>
|
||||||
</tr>
|
</x-tr>
|
||||||
@endforeach
|
@endforeach
|
||||||
</x-table>
|
</x-table>
|
||||||
@else
|
@else
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
<x-live id="live-database-users">
|
<x-live id="live-database-users">
|
||||||
@if (count($databaseUsers) > 0)
|
@if (count($databaseUsers) > 0)
|
||||||
<x-table>
|
<x-table>
|
||||||
<tr>
|
<x-tr>
|
||||||
<x-th>{{ __("Username") }}</x-th>
|
<x-th>{{ __("Username") }}</x-th>
|
||||||
<x-th>{{ __("Created") }}</x-th>
|
<x-th>{{ __("Created") }}</x-th>
|
||||||
<x-th class="flex items-center">
|
<x-th class="flex items-center">
|
||||||
@ -29,9 +29,9 @@
|
|||||||
</x-th>
|
</x-th>
|
||||||
<x-th>{{ __("Status") }}</x-th>
|
<x-th>{{ __("Status") }}</x-th>
|
||||||
<x-th></x-th>
|
<x-th></x-th>
|
||||||
</tr>
|
</x-tr>
|
||||||
@foreach ($databaseUsers as $databaseUser)
|
@foreach ($databaseUsers as $databaseUser)
|
||||||
<tr>
|
<x-tr>
|
||||||
<x-td>{{ $databaseUser->username }}</x-td>
|
<x-td>{{ $databaseUser->username }}</x-td>
|
||||||
<x-td>
|
<x-td>
|
||||||
<x-datetime :value="$databaseUser->created_at" />
|
<x-datetime :value="$databaseUser->created_at" />
|
||||||
@ -63,7 +63,7 @@
|
|||||||
<x-heroicon name="o-trash" class="h-5 w-5" />
|
<x-heroicon name="o-trash" class="h-5 w-5" />
|
||||||
</x-icon-button>
|
</x-icon-button>
|
||||||
</x-td>
|
</x-td>
|
||||||
</tr>
|
</x-tr>
|
||||||
@endforeach
|
@endforeach
|
||||||
</x-table>
|
</x-table>
|
||||||
@else
|
@else
|
||||||
|
@ -29,182 +29,37 @@
|
|||||||
@vite(["resources/css/app.css", "resources/js/app.js"])
|
@vite(["resources/css/app.css", "resources/js/app.js"])
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body
|
<body class="bg-gray-50 font-sans antialiased dark:bg-gray-900 dark:text-gray-300" x-data="" x-cloak>
|
||||||
class="min-h-screen min-w-max bg-gray-100 font-sans antialiased dark:bg-gray-900 dark:text-gray-300"
|
@include("layouts.partials.search")
|
||||||
x-data=""
|
|
||||||
x-cloak
|
|
||||||
>
|
|
||||||
<div class="flex min-h-screen">
|
|
||||||
<div
|
|
||||||
class="left-0 top-0 min-h-screen w-64 flex-none bg-gray-800 p-3 dark:border-r-2 dark:border-gray-800 dark:bg-gray-800/50"
|
|
||||||
>
|
|
||||||
<div class="block h-16">
|
|
||||||
<div class="flex items-center justify-start text-2xl font-extrabold text-white">
|
|
||||||
<x-application-logo class="h-7 w-7 rounded-md" />
|
|
||||||
<span class="ml-1">Deploy</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="mb-5">
|
|
||||||
<div class="text-sm font-semibold uppercase text-gray-300">
|
|
||||||
{{ __("Projects") }}
|
|
||||||
</div>
|
|
||||||
<div class="mt-2">
|
|
||||||
@include("layouts.partials.project-select", ["project" => auth()->user()->currentProject])
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="mt-5 text-sm font-semibold uppercase text-gray-300">
|
|
||||||
{{ __("Servers") }}
|
|
||||||
</div>
|
|
||||||
<div class="mt-2">
|
|
||||||
@include("layouts.partials.server-select", ["server" => $server ?? null])
|
|
||||||
</div>
|
|
||||||
|
|
||||||
@if (isset($server))
|
|
||||||
<div class="mt-3 space-y-1">
|
|
||||||
<x-sidebar-link
|
|
||||||
:href="route('servers.show', ['server' => $server])"
|
|
||||||
:active="request()->routeIs('servers.show')"
|
|
||||||
>
|
|
||||||
<x-heroicon name="o-home" class="h-6 w-6" />
|
|
||||||
<span class="ml-2 text-gray-50">
|
|
||||||
{{ __("Overview") }}
|
|
||||||
</span>
|
|
||||||
</x-sidebar-link>
|
|
||||||
@if ($server->webserver())
|
|
||||||
<x-sidebar-link
|
|
||||||
:href="route('servers.sites', ['server' => $server])"
|
|
||||||
:active="request()->routeIs('servers.sites') || request()->is('servers/*/sites/*')"
|
|
||||||
>
|
|
||||||
<x-heroicon name="o-globe-alt" class="h-6 w-6" />
|
|
||||||
<span class="ml-2 text-gray-50">
|
|
||||||
{{ __("Sites") }}
|
|
||||||
</span>
|
|
||||||
</x-sidebar-link>
|
|
||||||
@endif
|
|
||||||
|
|
||||||
@if ($server->database())
|
|
||||||
<x-sidebar-link
|
|
||||||
:href="route('servers.databases', ['server' => $server])"
|
|
||||||
:active="request()->routeIs('servers.databases') ||
|
|
||||||
request()->routeIs('servers.databases.backups')"
|
|
||||||
>
|
|
||||||
<x-heroicon name="o-circle-stack" class="h-6 w-6" />
|
|
||||||
<span class="ml-2 text-gray-50">
|
|
||||||
{{ __("Databases") }}
|
|
||||||
</span>
|
|
||||||
</x-sidebar-link>
|
|
||||||
@endif
|
|
||||||
|
|
||||||
@if ($server->php())
|
|
||||||
<x-sidebar-link
|
|
||||||
:href="route('servers.php', ['server' => $server])"
|
|
||||||
:active="request()->routeIs('servers.php')"
|
|
||||||
>
|
|
||||||
<x-heroicon name="o-code-bracket" class="h-6 w-6" />
|
|
||||||
<span class="ml-2 text-gray-50">
|
|
||||||
{{ __("PHP") }}
|
|
||||||
</span>
|
|
||||||
</x-sidebar-link>
|
|
||||||
@endif
|
|
||||||
|
|
||||||
@if ($server->firewall())
|
|
||||||
<x-sidebar-link
|
|
||||||
:href="route('servers.firewall', ['server' => $server])"
|
|
||||||
:active="request()->routeIs('servers.firewall')"
|
|
||||||
>
|
|
||||||
<x-heroicon name="o-fire" class="h-6 w-6" />
|
|
||||||
<span class="ml-2 text-gray-50">
|
|
||||||
{{ __("Firewall") }}
|
|
||||||
</span>
|
|
||||||
</x-sidebar-link>
|
|
||||||
@endif
|
|
||||||
|
|
||||||
<x-sidebar-link
|
|
||||||
:href="route('servers.cronjobs', ['server' => $server])"
|
|
||||||
:active="request()->routeIs('servers.cronjobs')"
|
|
||||||
>
|
|
||||||
<x-heroicon name="o-clock" class="h-6 w-6" />
|
|
||||||
<span class="ml-2 text-gray-50">
|
|
||||||
{{ __("Cronjobs") }}
|
|
||||||
</span>
|
|
||||||
</x-sidebar-link>
|
|
||||||
<x-sidebar-link
|
|
||||||
:href="route('servers.ssh-keys', ['server' => $server])"
|
|
||||||
:active="request()->routeIs('servers.ssh-keys')"
|
|
||||||
>
|
|
||||||
<x-heroicon name="o-key" class="h-6 w-6" />
|
|
||||||
<span class="ml-2 text-gray-50">
|
|
||||||
{{ __("SSH Keys") }}
|
|
||||||
</span>
|
|
||||||
</x-sidebar-link>
|
|
||||||
<x-sidebar-link
|
|
||||||
:href="route('servers.services', ['server' => $server])"
|
|
||||||
:active="request()->routeIs('servers.services')"
|
|
||||||
>
|
|
||||||
<x-heroicon name="o-cog-6-tooth" class="h-6 w-6" />
|
|
||||||
<span class="ml-2 text-gray-50">
|
|
||||||
{{ __("Services") }}
|
|
||||||
</span>
|
|
||||||
</x-sidebar-link>
|
|
||||||
|
|
||||||
<x-sidebar-link
|
|
||||||
:href="route('servers.settings', ['server' => $server])"
|
|
||||||
:active="request()->routeIs('servers.settings')"
|
|
||||||
>
|
|
||||||
<x-heroicon name="o-wrench-screwdriver" class="h-6 w-6" />
|
|
||||||
<span class="ml-2 text-gray-50">
|
|
||||||
{{ __("Settings") }}
|
|
||||||
</span>
|
|
||||||
</x-sidebar-link>
|
|
||||||
<x-sidebar-link
|
|
||||||
:href="route('servers.logs', ['server' => $server])"
|
|
||||||
:active="request()->routeIs('servers.logs')"
|
|
||||||
>
|
|
||||||
<x-heroicon name="o-square-3-stack-3d" class="h-6 w-6" />
|
|
||||||
<span class="ml-2 text-gray-50">
|
|
||||||
{{ __("Logs") }}
|
|
||||||
</span>
|
|
||||||
</x-sidebar-link>
|
|
||||||
</div>
|
|
||||||
@endif
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
@if (isset($sidebar))
|
|
||||||
<div
|
|
||||||
class="min-h-screen w-64 flex-none border-r border-gray-200 bg-white dark:border-gray-800 dark:bg-gray-900"
|
|
||||||
>
|
|
||||||
{{ $sidebar }}
|
|
||||||
</div>
|
|
||||||
@endif
|
|
||||||
|
|
||||||
<div class="flex min-h-screen flex-grow flex-col">
|
|
||||||
@include("layouts.navigation")
|
@include("layouts.navigation")
|
||||||
|
|
||||||
<!-- Page Heading -->
|
@include("layouts.sidebar")
|
||||||
|
|
||||||
|
<div class="mt-[64px] w-full"></div>
|
||||||
|
|
||||||
|
<div class="sm:ml-64">
|
||||||
@if (isset($header))
|
@if (isset($header))
|
||||||
<header class="border-b border-gray-200 bg-white dark:border-gray-800 dark:bg-gray-900">
|
<header class="border-b border-gray-200 bg-white dark:border-gray-700 dark:bg-gray-800">
|
||||||
<div class="mx-auto flex h-20 w-full max-w-full items-center justify-between px-8">
|
<div class="mx-auto flex h-20 w-full max-w-full items-center justify-between px-5">
|
||||||
{{ $header }}
|
{{ $header }}
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
@if (isset($header2))
|
@if (isset($header2))
|
||||||
<header class="border-b border-gray-200 bg-white dark:border-gray-800 dark:bg-gray-900">
|
<header class="border-b border-gray-200 bg-white dark:border-gray-700 dark:bg-gray-800">
|
||||||
<div class="mx-auto max-w-full px-8 py-6">
|
<div class="mx-auto max-w-full px-5 py-6">
|
||||||
{{ $header2 }}
|
{{ $header2 }}
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
<!-- Page Content -->
|
<div class="px-4 py-10">
|
||||||
<main class="px-8">
|
|
||||||
{{ $slot }}
|
{{ $slot }}
|
||||||
</main>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
// On page load or when changing themes, best to add inline in `head` to avoid FOUC
|
// On page load or when changing themes, best to add inline in `head` to avoid FOUC
|
||||||
if (
|
if (
|
||||||
|
@ -1,14 +1,79 @@
|
|||||||
<nav x-data="{ open: false }" class="h-16 border-b border-gray-200 bg-white dark:border-gray-800 dark:bg-gray-900">
|
<nav
|
||||||
<div class="mx-auto max-w-full px-4 sm:px-6 lg:px-8">
|
class="fixed top-0 z-50 flex h-[64px] w-full items-center border-b border-gray-200 bg-white dark:border-gray-700 dark:bg-gray-800"
|
||||||
<div class="flex h-16 items-center justify-between">
|
>
|
||||||
<div class="flex"></div>
|
<div class="w-full px-3 py-3 lg:px-5 lg:pl-3">
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<div class="flex items-center justify-start">
|
||||||
|
<button
|
||||||
|
data-drawer-target="logo-sidebar"
|
||||||
|
data-drawer-toggle="logo-sidebar"
|
||||||
|
aria-controls="logo-sidebar"
|
||||||
|
type="button"
|
||||||
|
class="inline-flex items-center rounded-md p-2 text-sm text-gray-500 hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-gray-200 dark:text-gray-400 dark:hover:bg-gray-700 dark:focus:ring-gray-600 sm:hidden"
|
||||||
|
>
|
||||||
|
<span class="sr-only">Open sidebar</span>
|
||||||
|
<x-heroicon name="o-bars-3-center-left" class="h-6 w-6" />
|
||||||
|
</button>
|
||||||
|
<a href="/" class="ms-2 flex md:me-24">
|
||||||
|
<div class="flex items-center justify-start text-3xl font-extrabold">
|
||||||
|
<x-application-logo class="h-9 w-9 rounded-md" />
|
||||||
|
<span class="ml-1 hidden sm:block">Deploy</span>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
<div class="h-[64px] w-1 border-r border-gray-200 px-3 dark:border-gray-700 md:px-0"></div>
|
||||||
|
<div class="ml-5 cursor-pointer" x-data="">
|
||||||
|
<div
|
||||||
|
class="flex w-full items-center rounded-md border border-gray-200 bg-gray-100 px-4 py-2 text-sm text-gray-900 focus:ring-4 focus:ring-gray-300 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-300 dark:focus:ring-gray-600"
|
||||||
|
@click="$dispatch('open-search')"
|
||||||
|
>
|
||||||
|
<x-heroicon name="o-magnifying-glass" class="h-4 w-4" />
|
||||||
|
<span class="ml-2 hidden lg:block">Press / to Search</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<div class="mr-3">
|
<div class="mr-3">
|
||||||
@include("layouts.partials.color-scheme")
|
@include("layouts.partials.color-scheme")
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<x-user-dropdown />
|
<x-dropdown align="right" width="48">
|
||||||
|
<x-slot name="trigger">
|
||||||
|
<button
|
||||||
|
class="flex rounded-full text-sm focus:ring-4 focus:ring-gray-300 dark:focus:ring-gray-600"
|
||||||
|
>
|
||||||
|
<x-heroicon name="o-cog-6-tooth" class="h-8 w-8 rounded-full" />
|
||||||
|
</button>
|
||||||
|
</x-slot>
|
||||||
|
<x-slot name="content">
|
||||||
|
<div class="px-4 py-3" role="none">
|
||||||
|
<p class="text-sm text-gray-900 dark:text-white" role="none">
|
||||||
|
{{ auth()->user()->name }}
|
||||||
|
</p>
|
||||||
|
<p class="truncate text-sm font-medium text-gray-900 dark:text-gray-300" role="none">
|
||||||
|
{{ auth()->user()->email }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<x-dropdown-link :href="route('profile')">
|
||||||
|
{{ __("Profile") }}
|
||||||
|
</x-dropdown-link>
|
||||||
|
|
||||||
|
<x-dropdown-link :href="route('projects')">
|
||||||
|
{{ __("Projects") }}
|
||||||
|
</x-dropdown-link>
|
||||||
|
|
||||||
|
<!-- Authentication -->
|
||||||
|
<form method="POST" action="{{ route("logout") }}">
|
||||||
|
@csrf
|
||||||
|
<x-dropdown-link
|
||||||
|
:href="route('logout')"
|
||||||
|
onclick="event.preventDefault();this.closest('form').submit();"
|
||||||
|
>
|
||||||
|
{{ __("Log Out") }}
|
||||||
|
</x-dropdown-link>
|
||||||
|
</form>
|
||||||
|
</x-slot>
|
||||||
|
</x-dropdown>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,124 +1,35 @@
|
|||||||
<div x-data="projectCombobox()">
|
<div data-tooltip="Project" class="cursor-pointer">
|
||||||
<div class="relative">
|
<x-dropdown align="left">
|
||||||
|
<x-slot:trigger>
|
||||||
|
<div>
|
||||||
<div
|
<div
|
||||||
@click="open = !open"
|
class="block w-full rounded-md border border-gray-300 bg-gray-50 p-2.5 text-sm text-gray-900 focus:border-primary-500 focus:ring-primary-500 dark:border-gray-600 dark:bg-gray-700 dark:text-white dark:placeholder-gray-400 dark:focus:border-primary-500 dark:focus:ring-primary-500"
|
||||||
class="text-md z-0 flex h-10 w-full cursor-pointer items-center rounded-md bg-gray-900 px-4 py-3 pr-10 leading-5 text-gray-100 focus:ring-1 focus:ring-gray-700"
|
|
||||||
x-text="selected.name ?? 'Select Project'"
|
|
||||||
></div>
|
|
||||||
<button type="button" @click="open = !open" class="absolute inset-y-0 right-0 z-0 flex items-center pr-2">
|
|
||||||
<svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
viewBox="0 0 20 20"
|
|
||||||
fill="currentColor"
|
|
||||||
aria-hidden="true"
|
|
||||||
class="h-5 w-5 text-gray-400"
|
|
||||||
>
|
>
|
||||||
<path
|
{{ auth()->user()->currentProject?->name ?? __("Select Project") }}
|
||||||
fill-rule="evenodd"
|
</div>
|
||||||
d="M10 3a.75.75 0 01.55.24l3.25 3.5a.75.75 0 11-1.1 1.02L10 4.852 7.3 7.76a.75.75 0 01-1.1-1.02l3.25-3.5A.75.75 0 0110 3zm-3.76 9.2a.75.75 0 011.06.04l2.7 2.908 2.7-2.908a.75.75 0 111.1 1.02l-3.25 3.5a.75.75 0 01-1.1 0l-3.25-3.5a.75.75 0 01.04-1.06z"
|
<button type="button" class="absolute inset-y-0 right-0 flex items-center pr-2">
|
||||||
clip-rule="evenodd"
|
<x-heroicon name="o-chevron-down" class="h-4 w-4 text-gray-400" />
|
||||||
></path>
|
|
||||||
</svg>
|
|
||||||
</button>
|
</button>
|
||||||
<div
|
|
||||||
x-show="open"
|
|
||||||
@click.away="open = false"
|
|
||||||
class="absolute z-10 mt-1 w-full overflow-auto rounded-md bg-white pb-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none dark:bg-gray-700 sm:text-sm"
|
|
||||||
>
|
|
||||||
<div class="relative p-2">
|
|
||||||
<input
|
|
||||||
x-model="query"
|
|
||||||
@input="filterProjectsAndOpen"
|
|
||||||
placeholder="Filter"
|
|
||||||
class="dark:focus:ring-800 w-full rounded-md bg-gray-200 py-2 pl-3 pr-10 text-sm leading-5 focus:ring-1 focus:ring-gray-400 dark:bg-gray-900 dark:text-gray-100"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="relative max-h-[350px] overflow-y-auto">
|
</x-slot>
|
||||||
<template x-for="(project, index) in filteredProjects" :key="index">
|
<x-slot:content>
|
||||||
<div
|
@foreach (auth()->user()->projects as $project)
|
||||||
@click="selectProject(project); open = false"
|
<x-dropdown-link class="relative" :href="route('projects.switch', ['project' => $project])">
|
||||||
:class="project.id === selected.id ? 'cursor-default bg-primary-600 text-white' : 'cursor-pointer'"
|
<span class="block truncate">{{ ucfirst($project->name) }}</span>
|
||||||
class="relative select-none px-4 py-2 text-gray-700 hover:bg-primary-600 hover:text-white dark:text-white"
|
@if ($project->id == auth()->user()->current_project_id)
|
||||||
>
|
<span class="absolute inset-y-0 right-0 flex items-center pr-3 text-primary-600">
|
||||||
<span class="block truncate" x-text="project.name"></span>
|
<x-heroicon name="o-check" class="h-5 w-5" />
|
||||||
<template x-if="project.id === selected.id">
|
|
||||||
<span class="absolute inset-y-0 right-0 flex items-center pr-3 text-white">
|
|
||||||
<svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
viewBox="0 0 20 20"
|
|
||||||
fill="currentColor"
|
|
||||||
aria-hidden="true"
|
|
||||||
class="h-5 w-5"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
fill-rule="evenodd"
|
|
||||||
d="M16.704 4.153a.75.75 0 01.143 1.052l-8 10.5a.75.75 0 01-1.127.075l-4.5-4.5a.75.75 0 011.06-1.06l3.894 3.893 7.48-9.817a.75.75 0 011.05-.143z"
|
|
||||||
clip-rule="evenodd"
|
|
||||||
></path>
|
|
||||||
</svg>
|
|
||||||
</span>
|
</span>
|
||||||
</template>
|
@endif
|
||||||
</div>
|
</x-dropdown-link>
|
||||||
</template>
|
@endforeach
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
x-show="filteredProjects.length === 0"
|
|
||||||
class="relative block cursor-default select-none truncate px-4 py-2 text-gray-700 dark:text-white"
|
|
||||||
>
|
|
||||||
No projects found!
|
|
||||||
</div>
|
|
||||||
<div class="py-1">
|
|
||||||
<hr class="border-gray-300 dark:border-gray-600" />
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<a
|
|
||||||
href="{{ route("projects") }}"
|
|
||||||
class="relative block cursor-pointer select-none px-4 py-2 text-gray-700 hover:bg-primary-600 hover:text-white dark:text-white"
|
|
||||||
>
|
|
||||||
<span class="block truncate">Projects List</span>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<a
|
|
||||||
href="{{ route("projects", ["create" => true]) }}"
|
|
||||||
class="relative block cursor-pointer select-none px-4 py-2 text-gray-700 hover:bg-primary-600 hover:text-white dark:text-white"
|
|
||||||
>
|
|
||||||
<span class="block truncate">Create a Project</span>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script>
|
<x-dropdown-link href="{{ route('projects') }}">
|
||||||
function projectCombobox() {
|
{{ __("Projects List") }}
|
||||||
const projects = @json(auth()->user()->projects()->select('id', 'name')->get());
|
</x-dropdown-link>
|
||||||
return {
|
<x-dropdown-link href="{{ route('projects', ['create' => 'open']) }}">
|
||||||
open: false,
|
{{ __("Create a Project") }}
|
||||||
query: '',
|
</x-dropdown-link>
|
||||||
projects: projects,
|
</x-slot>
|
||||||
selected: @if(isset($project)) @json($project->only('id', 'name')) @else {} @endif,
|
</x-dropdown>
|
||||||
filteredProjects: projects,
|
</div>
|
||||||
selectProject(project) {
|
|
||||||
if (this.selected.id !== project.id) {
|
|
||||||
this.selected = project;
|
|
||||||
window.location.href = '{{ url('/settings/projects/switch') }}/' + project.id
|
|
||||||
}
|
|
||||||
},
|
|
||||||
filterProjectsAndOpen() {
|
|
||||||
if (this.query === '') {
|
|
||||||
this.filteredProjects = this.projects;
|
|
||||||
this.open = false;
|
|
||||||
} else {
|
|
||||||
this.filteredProjects = this.projects.filter((project) =>
|
|
||||||
project.name
|
|
||||||
.toLowerCase()
|
|
||||||
.replace(/\s+/g, '')
|
|
||||||
.includes(this.query.toLowerCase().replace(/\s+/g, ''))
|
|
||||||
);
|
|
||||||
this.open = true;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
193
resources/views/layouts/partials/search.blade.php
Normal file
@ -0,0 +1,193 @@
|
|||||||
|
<div
|
||||||
|
x-data="{
|
||||||
|
open: false,
|
||||||
|
isTyping: false,
|
||||||
|
focused: false,
|
||||||
|
searchQuery: '',
|
||||||
|
isLoading: false,
|
||||||
|
searchResult: [],
|
||||||
|
noResults: false,
|
||||||
|
showResults: false,
|
||||||
|
keyUp: false,
|
||||||
|
focusedItem: null,
|
||||||
|
init() {
|
||||||
|
document.onkeydown = (e) => {
|
||||||
|
if (e.key === '/') {
|
||||||
|
if (
|
||||||
|
! document.activeElement.type ||
|
||||||
|
document.activeElement.type === 'undefined'
|
||||||
|
) {
|
||||||
|
this.openSearch()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (e.key === 'Escape') {
|
||||||
|
this.close()
|
||||||
|
} else {
|
||||||
|
this.changeFocus(e.code)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$watch(
|
||||||
|
'searchQuery',
|
||||||
|
this.debounce(() => {
|
||||||
|
this.isTyping = false
|
||||||
|
}, 700),
|
||||||
|
)
|
||||||
|
$watch('isTyping', (value) => {
|
||||||
|
if (! value && this.keyUp && this.searchQuery.length > 0) {
|
||||||
|
this.search(this.searchQuery)
|
||||||
|
}
|
||||||
|
if (! value && this.searchQuery.length === 0) {
|
||||||
|
this.showResults = false
|
||||||
|
this.searchResult = []
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
close() {
|
||||||
|
this.open = false
|
||||||
|
this.isLoading = false
|
||||||
|
this.focused = false
|
||||||
|
this.focusedItem = null
|
||||||
|
document.body.classList.remove('overflow-y-hidden')
|
||||||
|
},
|
||||||
|
openSearch() {
|
||||||
|
this.open = true
|
||||||
|
document.body.classList.add('overflow-y-hidden')
|
||||||
|
setTimeout(() => {
|
||||||
|
this.$refs.input.select()
|
||||||
|
}, 50)
|
||||||
|
},
|
||||||
|
search(searchQuery) {
|
||||||
|
this.showResults = true
|
||||||
|
this.noResults = false
|
||||||
|
this.isLoading = true
|
||||||
|
fetch('/search?q=' + searchQuery)
|
||||||
|
.then((response) => response.json())
|
||||||
|
.then((data) => {
|
||||||
|
this.searchResult = data.results
|
||||||
|
if (this.searchResult.length === 0) {
|
||||||
|
this.noResults = true
|
||||||
|
}
|
||||||
|
this.keyUp = false
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error('Error:', error)
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
this.isLoading = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
keyEntered(e) {
|
||||||
|
this.keyUp = true
|
||||||
|
if (e.code === 'Escape') {
|
||||||
|
this.close()
|
||||||
|
}
|
||||||
|
this.changeFocus(e.code)
|
||||||
|
},
|
||||||
|
changeFocus(type) {
|
||||||
|
if (type === 'Backspace' && this.open && ! this.focused) {
|
||||||
|
setTimeout(() => {
|
||||||
|
this.$refs.input.select()
|
||||||
|
}, 100)
|
||||||
|
}
|
||||||
|
if (type === 'ArrowDown') {
|
||||||
|
if (this.searchResult.length > 0) {
|
||||||
|
if (this.focusedItem !== null) {
|
||||||
|
if (this.focusedItem < this.searchResult.length - 1) {
|
||||||
|
this.focusedItem++
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.focusedItem = 0
|
||||||
|
}
|
||||||
|
document.getElementById(`result-${this.focusedItem}`).focus()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (type === 'ArrowUp') {
|
||||||
|
if (this.searchResult.length > 0) {
|
||||||
|
if (this.focusedItem !== null) {
|
||||||
|
if (this.focusedItem > 0) {
|
||||||
|
this.focusedItem--
|
||||||
|
}
|
||||||
|
document
|
||||||
|
.getElementById(`result-${this.focusedItem}`)
|
||||||
|
.focus()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
debounce(func, wait, immediate) {
|
||||||
|
let timeout
|
||||||
|
return function () {
|
||||||
|
const context = this,
|
||||||
|
args = arguments
|
||||||
|
const later = function () {
|
||||||
|
timeout = null
|
||||||
|
if (! immediate) func.apply(context, args)
|
||||||
|
}
|
||||||
|
const callNow = immediate && ! timeout
|
||||||
|
clearTimeout(timeout)
|
||||||
|
timeout = setTimeout(later, wait)
|
||||||
|
if (callNow) func.apply(context, args)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}"
|
||||||
|
@open-search.window="openSearch"
|
||||||
|
>
|
||||||
|
<div x-show="open" class="absolute bottom-0 left-0 right-0 top-0 flex max-w-full items-start justify-center">
|
||||||
|
<div
|
||||||
|
x-on:click="close"
|
||||||
|
class="fixed inset-0 bottom-0 left-0 right-0 top-0 z-[1000] items-center bg-gray-500 opacity-75 dark:bg-gray-900"
|
||||||
|
></div>
|
||||||
|
<div class="absolute z-[1000] mt-20 lg:scale-110">
|
||||||
|
<div class="w-[500px]">
|
||||||
|
<x-text-input
|
||||||
|
id="search-input"
|
||||||
|
x-ref="input"
|
||||||
|
@focus="focused = true"
|
||||||
|
@blur="focused = false"
|
||||||
|
type="text"
|
||||||
|
class="w-full"
|
||||||
|
@input="isTyping = true"
|
||||||
|
x-model="searchQuery"
|
||||||
|
placeholder="Search something..."
|
||||||
|
@keyup="keyEntered"
|
||||||
|
autocomplete="off"
|
||||||
|
></x-text-input>
|
||||||
|
<div
|
||||||
|
x-show="showResults"
|
||||||
|
class="list-none divide-y divide-gray-100 rounded-md bg-white py-1 text-base shadow ring-1 ring-black ring-opacity-5 dark:divide-gray-700/50 dark:bg-gray-800"
|
||||||
|
>
|
||||||
|
<div x-show="isLoading" class="w-full px-3 py-2 text-sm text-gray-800 dark:text-gray-300">
|
||||||
|
Please wait
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
x-show="!isLoading && noResults"
|
||||||
|
class="w-full px-3 py-2 text-sm text-gray-800 dark:text-gray-300"
|
||||||
|
>
|
||||||
|
No results
|
||||||
|
</div>
|
||||||
|
<template x-for="(item, index) in searchResult">
|
||||||
|
<button
|
||||||
|
x-bind:id="`result-${index}`"
|
||||||
|
class="flex w-full items-center justify-between p-3 text-left text-sm leading-5 text-gray-700 transition duration-150 ease-in-out hover:bg-gray-100 focus:bg-gray-100 focus:outline-none dark:text-gray-300 dark:hover:bg-gray-700/50 dark:focus:bg-gray-700"
|
||||||
|
x-on:click="window.location.href = item.url"
|
||||||
|
>
|
||||||
|
<div class="font-semibold text-primary-500" x-text="item.text"></div>
|
||||||
|
<div class="flex items-center">
|
||||||
|
<span
|
||||||
|
class="mr-1 rounded-xl bg-gray-100 px-2 text-gray-500 dark:bg-gray-700 dark:text-gray-400"
|
||||||
|
x-text="item.project"
|
||||||
|
data-tooltip="Project"
|
||||||
|
></span>
|
||||||
|
<span
|
||||||
|
class="rounded-xl bg-primary-100 px-2 text-primary-500 dark:bg-primary-500 dark:bg-opacity-10"
|
||||||
|
x-text="item.type"
|
||||||
|
data-tooltip="Type"
|
||||||
|
></span>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
@ -1,124 +1,35 @@
|
|||||||
<div x-data="serverCombobox()">
|
<div data-tooltip="Servers" class="cursor-pointer">
|
||||||
<div class="relative">
|
<x-dropdown width="full">
|
||||||
|
<x-slot:trigger>
|
||||||
|
<div>
|
||||||
<div
|
<div
|
||||||
@click="open = !open"
|
class="block w-full rounded-md border border-gray-300 bg-gray-50 p-2.5 text-sm text-gray-900 focus:border-primary-500 focus:ring-primary-500 dark:border-gray-600 dark:bg-gray-700 dark:text-white dark:placeholder-gray-400 dark:focus:border-primary-500 dark:focus:ring-primary-500"
|
||||||
class="text-md z-0 flex h-10 w-full cursor-pointer items-center rounded-md bg-gray-900 px-4 py-3 pr-10 leading-5 text-gray-100 focus:ring-1 focus:ring-gray-700"
|
|
||||||
x-text="selected.name ?? 'Select Server'"
|
|
||||||
></div>
|
|
||||||
<button type="button" @click="open = !open" class="absolute inset-y-0 right-0 z-0 flex items-center pr-2">
|
|
||||||
<svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
viewBox="0 0 20 20"
|
|
||||||
fill="currentColor"
|
|
||||||
aria-hidden="true"
|
|
||||||
class="h-5 w-5 text-gray-400"
|
|
||||||
>
|
>
|
||||||
<path
|
{{ isset($server) ? $server->name : "Select Server" }}
|
||||||
fill-rule="evenodd"
|
</div>
|
||||||
d="M10 3a.75.75 0 01.55.24l3.25 3.5a.75.75 0 11-1.1 1.02L10 4.852 7.3 7.76a.75.75 0 01-1.1-1.02l3.25-3.5A.75.75 0 0110 3zm-3.76 9.2a.75.75 0 011.06.04l2.7 2.908 2.7-2.908a.75.75 0 111.1 1.02l-3.25 3.5a.75.75 0 01-1.1 0l-3.25-3.5a.75.75 0 01.04-1.06z"
|
<button type="button" class="absolute inset-y-0 right-0 flex items-center pr-2">
|
||||||
clip-rule="evenodd"
|
<x-heroicon name="o-chevron-down" class="h-4 w-4 text-gray-400" />
|
||||||
></path>
|
|
||||||
</svg>
|
|
||||||
</button>
|
</button>
|
||||||
<div
|
|
||||||
x-show="open"
|
|
||||||
@click.away="open = false"
|
|
||||||
class="absolute z-10 mt-1 w-full overflow-auto rounded-md bg-white pb-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none dark:bg-gray-700 sm:text-sm"
|
|
||||||
>
|
|
||||||
<div class="relative p-2">
|
|
||||||
<input
|
|
||||||
x-model="query"
|
|
||||||
@input="filterServersAndOpen"
|
|
||||||
placeholder="Filter"
|
|
||||||
class="dark:focus:ring-800 w-full rounded-md bg-gray-200 py-2 pl-3 pr-10 text-sm leading-5 focus:ring-1 focus:ring-gray-400 dark:bg-gray-900 dark:text-gray-100"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="relative max-h-[350px] overflow-y-auto">
|
</x-slot>
|
||||||
<template x-for="(server, index) in filteredServers" :key="index">
|
<x-slot:content>
|
||||||
<div
|
@foreach (auth()->user()->currentProject->servers as $s)
|
||||||
@click="selectServer(server); open = false"
|
<x-dropdown-link class="relative" :href="route('servers.show', ['server' => $s])">
|
||||||
:class="server.id === selected.id ? 'cursor-default bg-primary-600 text-white' : 'cursor-pointer'"
|
<span class="block truncate">{{ ucfirst($s->name) }}</span>
|
||||||
class="relative select-none px-4 py-2 text-gray-700 hover:bg-primary-600 hover:text-white dark:text-white"
|
@if (isset($server) && $server->id == $s->id)
|
||||||
>
|
<span class="absolute inset-y-0 right-0 flex items-center pr-3 text-primary-600">
|
||||||
<span class="block truncate" x-text="server.name"></span>
|
<x-heroicon name="o-check" class="h-5 w-5" />
|
||||||
<template x-if="server.id === selected.id">
|
|
||||||
<span class="absolute inset-y-0 right-0 flex items-center pr-3 text-white">
|
|
||||||
<svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
viewBox="0 0 20 20"
|
|
||||||
fill="currentColor"
|
|
||||||
aria-hidden="true"
|
|
||||||
class="h-5 w-5"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
fill-rule="evenodd"
|
|
||||||
d="M16.704 4.153a.75.75 0 01.143 1.052l-8 10.5a.75.75 0 01-1.127.075l-4.5-4.5a.75.75 0 011.06-1.06l3.894 3.893 7.48-9.817a.75.75 0 011.05-.143z"
|
|
||||||
clip-rule="evenodd"
|
|
||||||
></path>
|
|
||||||
</svg>
|
|
||||||
</span>
|
</span>
|
||||||
</template>
|
@endif
|
||||||
</div>
|
</x-dropdown-link>
|
||||||
</template>
|
@endforeach
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
x-show="filteredServers.length === 0"
|
|
||||||
class="relative block cursor-default select-none truncate px-4 py-2 text-gray-700 dark:text-white"
|
|
||||||
>
|
|
||||||
No servers found!
|
|
||||||
</div>
|
|
||||||
<div class="py-1">
|
|
||||||
<hr class="border-gray-300 dark:border-gray-600" />
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<a
|
|
||||||
href="{{ route("servers") }}"
|
|
||||||
class="@if(request()->routeIs('servers')) cursor-default bg-primary-600 text-white @else cursor-pointer @endif relative block select-none px-4 py-2 text-gray-700 hover:bg-primary-600 hover:text-white dark:text-white"
|
|
||||||
>
|
|
||||||
<span class="block truncate">Servers List</span>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<a
|
|
||||||
href="{{ route("servers.create") }}"
|
|
||||||
class="@if(request()->routeIs('servers.create')) cursor-default bg-primary-600 text-white @else cursor-pointer @endif relative block select-none px-4 py-2 text-gray-700 hover:bg-primary-600 hover:text-white dark:text-white"
|
|
||||||
>
|
|
||||||
<span class="block truncate">Create a Server</span>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script>
|
<x-dropdown-link href="{{ route('servers') }}">
|
||||||
function serverCombobox() {
|
{{ __("Servers List") }}
|
||||||
const servers = @json(auth()->user()->currentProject->servers()->select('id', 'name')->get());
|
</x-dropdown-link>
|
||||||
return {
|
<x-dropdown-link href="{{ route('servers.create') }}">
|
||||||
open: false,
|
{{ __("Create a Server") }}
|
||||||
query: '',
|
</x-dropdown-link>
|
||||||
servers: servers,
|
</x-slot>
|
||||||
selected: @if(isset($server)) @json($server->only('id', 'name')) @else {} @endif,
|
</x-dropdown>
|
||||||
filteredServers: servers,
|
</div>
|
||||||
selectServer(server) {
|
|
||||||
if (this.selected.id !== server.id) {
|
|
||||||
this.selected = server;
|
|
||||||
window.location.href = '{{ url('/servers/') }}/' + server.id
|
|
||||||
}
|
|
||||||
},
|
|
||||||
filterServersAndOpen() {
|
|
||||||
if (this.query === '') {
|
|
||||||
this.filteredServers = this.servers;
|
|
||||||
this.open = false;
|
|
||||||
} else {
|
|
||||||
this.filteredServers = this.servers.filter((server) =>
|
|
||||||
server.name
|
|
||||||
.toLowerCase()
|
|
||||||
.replace(/\s+/g, '')
|
|
||||||
.includes(this.query.toLowerCase().replace(/\s+/g, ''))
|
|
||||||
);
|
|
||||||
this.open = true;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
@ -3,147 +3,6 @@
|
|||||||
<x-slot name="pageTitle">{{ $pageTitle }}</x-slot>
|
<x-slot name="pageTitle">{{ $pageTitle }}</x-slot>
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
<x-slot name="sidebar">
|
|
||||||
<div class="flex h-16 items-center justify-center border-b border-gray-200 px-3 py-2 dark:border-gray-800">
|
|
||||||
<div class="w-full">
|
|
||||||
<span>Account Settings</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="space-y-2 p-3">
|
|
||||||
<x-secondary-sidebar-link :href="route('profile')" :active="request()->routeIs('profile')">
|
|
||||||
<svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
fill="none"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
stroke-width="1.5"
|
|
||||||
stroke="currentColor"
|
|
||||||
class="mr-2 h-6 w-6"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
d="M17.982 18.725A7.488 7.488 0 0012 15.75a7.488 7.488 0 00-5.982 2.975m11.963 0a9 9 0 10-11.963 0m11.963 0A8.966 8.966 0 0112 21a8.966 8.966 0 01-5.982-2.275M15 9.75a3 3 0 11-6 0 3 3 0 016 0z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
{{ __("Profile") }}
|
|
||||||
</x-secondary-sidebar-link>
|
|
||||||
<x-secondary-sidebar-link :href="route('projects')" :active="request()->routeIs('projects')">
|
|
||||||
<svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
fill="none"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
stroke-width="1.5"
|
|
||||||
stroke="currentColor"
|
|
||||||
class="mr-2 h-6 w-6"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
d="m7.875 14.25 1.214 1.942a2.25 2.25 0 0 0 1.908 1.058h2.006c.776 0 1.497-.4 1.908-1.058l1.214-1.942M2.41 9h4.636a2.25 2.25 0 0 1 1.872 1.002l.164.246a2.25 2.25 0 0 0 1.872 1.002h2.092a2.25 2.25 0 0 0 1.872-1.002l.164-.246A2.25 2.25 0 0 1 16.954 9h4.636M2.41 9a2.25 2.25 0 0 0-.16.832V12a2.25 2.25 0 0 0 2.25 2.25h15A2.25 2.25 0 0 0 21.75 12V9.832c0-.287-.055-.57-.16-.832M2.41 9a2.25 2.25 0 0 1 .382-.632l3.285-3.832a2.25 2.25 0 0 1 1.708-.786h8.43c.657 0 1.281.287 1.709.786l3.284 3.832c.163.19.291.404.382.632M4.5 20.25h15A2.25 2.25 0 0 0 21.75 18v-2.625c0-.621-.504-1.125-1.125-1.125H3.375c-.621 0-1.125.504-1.125 1.125V18a2.25 2.25 0 0 0 2.25 2.25Z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
{{ __("Projects") }}
|
|
||||||
</x-secondary-sidebar-link>
|
|
||||||
<x-secondary-sidebar-link
|
|
||||||
:href="route('server-providers')"
|
|
||||||
:active="request()->routeIs('server-providers')"
|
|
||||||
>
|
|
||||||
<svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
fill="none"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
stroke-width="1.5"
|
|
||||||
stroke="currentColor"
|
|
||||||
class="mr-2 h-6 w-6"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
d="M5.25 14.25h13.5m-13.5 0a3 3 0 01-3-3m3 3a3 3 0 100 6h13.5a3 3 0 100-6m-16.5-3a3 3 0 013-3h13.5a3 3 0 013 3m-19.5 0a4.5 4.5 0 01.9-2.7L5.737 5.1a3.375 3.375 0 012.7-1.35h7.126c1.062 0 2.062.5 2.7 1.35l2.587 3.45a4.5 4.5 0 01.9 2.7m0 0a3 3 0 01-3 3m0 3h.008v.008h-.008v-.008zm0-6h.008v.008h-.008v-.008zm-3 6h.008v.008h-.008v-.008zm0-6h.008v.008h-.008v-.008z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
{{ __("Server Providers") }}
|
|
||||||
</x-secondary-sidebar-link>
|
|
||||||
<x-secondary-sidebar-link
|
|
||||||
:href="route('source-controls')"
|
|
||||||
:active="request()->routeIs('source-controls')"
|
|
||||||
>
|
|
||||||
<svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
fill="none"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
stroke-width="1.5"
|
|
||||||
stroke="currentColor"
|
|
||||||
class="mr-2 h-6 w-6"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
d="M14.25 9.75L16.5 12l-2.25 2.25m-4.5 0L7.5 12l2.25-2.25M6 20.25h12A2.25 2.25 0 0020.25 18V6A2.25 2.25 0 0018 3.75H6A2.25 2.25 0 003.75 6v12A2.25 2.25 0 006 20.25z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
{{ __("Source Controls") }}
|
|
||||||
</x-secondary-sidebar-link>
|
|
||||||
<x-secondary-sidebar-link
|
|
||||||
:href="route('storage-providers')"
|
|
||||||
:active="request()->routeIs('storage-providers')"
|
|
||||||
>
|
|
||||||
<svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
fill="none"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
stroke-width="1.5"
|
|
||||||
stroke="currentColor"
|
|
||||||
class="mr-2 h-6 w-6"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
d="M20.25 6.375c0 2.278-3.694 4.125-8.25 4.125S3.75 8.653 3.75 6.375m16.5 0c0-2.278-3.694-4.125-8.25-4.125S3.75 4.097 3.75 6.375m16.5 0v11.25c0 2.278-3.694 4.125-8.25 4.125s-8.25-1.847-8.25-4.125V6.375m16.5 0v3.75m-16.5-3.75v3.75m16.5 0v3.75C20.25 16.153 16.556 18 12 18s-8.25-1.847-8.25-4.125v-3.75m16.5 0c0 2.278-3.694 4.125-8.25 4.125s-8.25-1.847-8.25-4.125"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
{{ __("Storage Providers") }}
|
|
||||||
</x-secondary-sidebar-link>
|
|
||||||
<x-secondary-sidebar-link
|
|
||||||
:href="route('notification-channels')"
|
|
||||||
:active="request()->routeIs('notification-channels')"
|
|
||||||
>
|
|
||||||
<svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
fill="none"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
stroke-width="1.5"
|
|
||||||
stroke="currentColor"
|
|
||||||
class="mr-2 h-6 w-6"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
d="M14.857 17.082a23.848 23.848 0 005.454-1.31A8.967 8.967 0 0118 9.75v-.7V9A6 6 0 006 9v.75a8.967 8.967 0 01-2.312 6.022c1.733.64 3.56 1.085 5.455 1.31m5.714 0a24.255 24.255 0 01-5.714 0m5.714 0a3 3 0 11-5.714 0"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
{{ __("Notification Channels") }}
|
|
||||||
</x-secondary-sidebar-link>
|
|
||||||
<x-secondary-sidebar-link :href="route('ssh-keys')" :active="request()->routeIs('ssh-keys')">
|
|
||||||
<svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
fill="none"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
stroke-width="1.5"
|
|
||||||
stroke="currentColor"
|
|
||||||
class="mr-2 h-6 w-6"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
d="M15.75 5.25a3 3 0 013 3m3 0a6 6 0 01-7.029 5.912c-.563-.097-1.159.026-1.563.43L10.5 17.25H8.25v2.25H6v2.25H2.25v-2.818c0-.597.237-1.17.659-1.591l6.499-6.499c.404-.404.527-1 .43-1.563A6 6 0 1121.75 8.25z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
{{ __("SSH Keys") }}
|
|
||||||
</x-secondary-sidebar-link>
|
|
||||||
</div>
|
|
||||||
</x-slot>
|
|
||||||
|
|
||||||
<x-container class="flex">
|
<x-container class="flex">
|
||||||
<div class="w-full">
|
<div class="w-full">
|
||||||
{{ $slot }}
|
{{ $slot }}
|
||||||
|
192
resources/views/layouts/sidebar.blade.php
Normal file
@ -0,0 +1,192 @@
|
|||||||
|
<aside
|
||||||
|
id="logo-sidebar"
|
||||||
|
class="fixed left-0 top-0 z-40 h-screen w-64 -translate-x-full border-r border-gray-200 bg-white pt-20 transition-transform dark:border-gray-700 dark:bg-gray-800 sm:translate-x-0"
|
||||||
|
aria-label="Sidebar"
|
||||||
|
>
|
||||||
|
<div class="h-full overflow-y-auto bg-white px-3 pb-4 dark:bg-gray-800">
|
||||||
|
<ul class="space-y-2 font-medium">
|
||||||
|
<li x-data="">
|
||||||
|
@include("layouts.partials.project-select")
|
||||||
|
</li>
|
||||||
|
<x-hr />
|
||||||
|
<li>
|
||||||
|
@include("layouts.partials.server-select")
|
||||||
|
</li>
|
||||||
|
<x-hr />
|
||||||
|
@if (isset($server))
|
||||||
|
<li>
|
||||||
|
<x-sidebar-link
|
||||||
|
:href="route('servers.show', ['server' => $server])"
|
||||||
|
:active="request()->routeIs('servers.show')"
|
||||||
|
>
|
||||||
|
<x-heroicon name="o-home" class="h-6 w-6" />
|
||||||
|
<span class="ml-2">Overview</span>
|
||||||
|
</x-sidebar-link>
|
||||||
|
</li>
|
||||||
|
@if ($server->webserver())
|
||||||
|
<li>
|
||||||
|
<x-sidebar-link
|
||||||
|
:href="route('servers.sites', ['server' => $server])"
|
||||||
|
:active="request()->routeIs('servers.sites') || request()->is('servers/*/sites/*')"
|
||||||
|
>
|
||||||
|
<x-heroicon name="o-globe-alt" class="h-6 w-6" />
|
||||||
|
<span class="ml-2">
|
||||||
|
{{ __("Sites") }}
|
||||||
|
</span>
|
||||||
|
</x-sidebar-link>
|
||||||
|
</li>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
@if ($server->database())
|
||||||
|
<li>
|
||||||
|
<x-sidebar-link
|
||||||
|
:href="route('servers.databases', ['server' => $server])"
|
||||||
|
:active="request()->routeIs('servers.databases') ||
|
||||||
|
request()->routeIs('servers.databases.backups')"
|
||||||
|
>
|
||||||
|
<x-heroicon name="o-circle-stack" class="h-6 w-6" />
|
||||||
|
<span class="ml-2">
|
||||||
|
{{ __("Databases") }}
|
||||||
|
</span>
|
||||||
|
</x-sidebar-link>
|
||||||
|
</li>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
@if ($server->php())
|
||||||
|
<li>
|
||||||
|
<x-sidebar-link
|
||||||
|
:href="route('servers.php', ['server' => $server])"
|
||||||
|
:active="request()->routeIs('servers.php')"
|
||||||
|
>
|
||||||
|
<x-heroicon name="o-code-bracket" class="h-6 w-6" />
|
||||||
|
<span class="ml-2">
|
||||||
|
{{ __("PHP") }}
|
||||||
|
</span>
|
||||||
|
</x-sidebar-link>
|
||||||
|
</li>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
@if ($server->firewall())
|
||||||
|
<li>
|
||||||
|
<x-sidebar-link
|
||||||
|
:href="route('servers.firewall', ['server' => $server])"
|
||||||
|
:active="request()->routeIs('servers.firewall')"
|
||||||
|
>
|
||||||
|
<x-heroicon name="o-fire" class="h-6 w-6" />
|
||||||
|
<span class="ml-2">
|
||||||
|
{{ __("Firewall") }}
|
||||||
|
</span>
|
||||||
|
</x-sidebar-link>
|
||||||
|
</li>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
<li>
|
||||||
|
<x-sidebar-link
|
||||||
|
:href="route('servers.cronjobs', ['server' => $server])"
|
||||||
|
:active="request()->routeIs('servers.cronjobs')"
|
||||||
|
>
|
||||||
|
<x-heroicon name="o-clock" class="h-6 w-6" />
|
||||||
|
<span class="ml-2">
|
||||||
|
{{ __("Cronjobs") }}
|
||||||
|
</span>
|
||||||
|
</x-sidebar-link>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li>
|
||||||
|
<x-sidebar-link
|
||||||
|
:href="route('servers.ssh-keys', ['server' => $server])"
|
||||||
|
:active="request()->routeIs('servers.ssh-keys')"
|
||||||
|
>
|
||||||
|
<x-heroicon name="o-key" class="h-6 w-6" />
|
||||||
|
<span class="ml-2">
|
||||||
|
{{ __("Server SSH Keys") }}
|
||||||
|
</span>
|
||||||
|
</x-sidebar-link>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li>
|
||||||
|
<x-sidebar-link
|
||||||
|
:href="route('servers.services', ['server' => $server])"
|
||||||
|
:active="request()->routeIs('servers.services')"
|
||||||
|
>
|
||||||
|
<x-heroicon name="o-cog-6-tooth" class="h-6 w-6" />
|
||||||
|
<span class="ml-2">
|
||||||
|
{{ __("Services") }}
|
||||||
|
</span>
|
||||||
|
</x-sidebar-link>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li>
|
||||||
|
<x-sidebar-link
|
||||||
|
:href="route('servers.settings', ['server' => $server])"
|
||||||
|
:active="request()->routeIs('servers.settings')"
|
||||||
|
>
|
||||||
|
<x-heroicon name="o-wrench-screwdriver" class="h-6 w-6" />
|
||||||
|
<span class="ml-2">
|
||||||
|
{{ __("Settings") }}
|
||||||
|
</span>
|
||||||
|
</x-sidebar-link>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li>
|
||||||
|
<x-sidebar-link
|
||||||
|
:href="route('servers.logs', ['server' => $server])"
|
||||||
|
:active="request()->routeIs('servers.logs')"
|
||||||
|
>
|
||||||
|
<x-heroicon name="o-square-3-stack-3d" class="h-6 w-6" />
|
||||||
|
<span class="ml-2">
|
||||||
|
{{ __("Logs") }}
|
||||||
|
</span>
|
||||||
|
</x-sidebar-link>
|
||||||
|
</li>
|
||||||
|
<x-hr />
|
||||||
|
@endif
|
||||||
|
|
||||||
|
<li>
|
||||||
|
<x-sidebar-link :href="route('profile')" :active="request()->routeIs('profile')">
|
||||||
|
<x-heroicon name="o-user-circle" class="h-6 w-6" />
|
||||||
|
<span class="ml-2">Profile</span>
|
||||||
|
</x-sidebar-link>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<x-sidebar-link :href="route('projects')" :active="request()->routeIs('projects')">
|
||||||
|
<x-heroicon name="o-inbox-stack" class="h-6 w-6" />
|
||||||
|
<span class="ml-2">Projects</span>
|
||||||
|
</x-sidebar-link>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<x-sidebar-link :href="route('server-providers')" :active="request()->routeIs('server-providers')">
|
||||||
|
<x-heroicon name="o-server-stack" class="h-6 w-6" />
|
||||||
|
<span class="ml-2">Server Providers</span>
|
||||||
|
</x-sidebar-link>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<x-sidebar-link :href="route('source-controls')" :active="request()->routeIs('source-controls')">
|
||||||
|
<x-heroicon name="o-code-bracket" class="h-6 w-6" />
|
||||||
|
<span class="ml-2">Source Controls</span>
|
||||||
|
</x-sidebar-link>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<x-sidebar-link :href="route('storage-providers')" :active="request()->routeIs('storage-providers')">
|
||||||
|
<x-heroicon name="o-circle-stack" class="h-6 w-6" />
|
||||||
|
<span class="ml-2">Storage Providers</span>
|
||||||
|
</x-sidebar-link>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<x-sidebar-link
|
||||||
|
:href="route('notification-channels')"
|
||||||
|
:active="request()->routeIs('notification-channels')"
|
||||||
|
>
|
||||||
|
<x-heroicon name="o-bell" class="h-6 w-6" />
|
||||||
|
<span class="ml-2">Notification Channels</span>
|
||||||
|
</x-sidebar-link>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<x-sidebar-link :href="route('ssh-keys')" :active="request()->routeIs('ssh-keys')">
|
||||||
|
<x-heroicon name="o-key" class="h-6 w-6" />
|
||||||
|
<span class="ml-2">SSH Keys</span>
|
||||||
|
</x-sidebar-link>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</aside>
|
@ -8,11 +8,111 @@
|
|||||||
@endif
|
@endif
|
||||||
|
|
||||||
<x-slot name="header">
|
<x-slot name="header">
|
||||||
<h2 class="text-lg font-semibold">
|
<div class="hidden md:flex md:items-center md:justify-start">
|
||||||
<a href="{{ $site->getUrl() }}" target="_blank">
|
<x-tab-item
|
||||||
{{ $site->domain }}
|
class="mr-1"
|
||||||
</a>
|
:href="route('servers.sites.show', ['server' => $site->server, 'site' => $site])"
|
||||||
</h2>
|
:active="request()->routeIs('servers.sites.show')"
|
||||||
|
>
|
||||||
|
<x-heroicon name="o-globe-alt" class="h-5 w-5" />
|
||||||
|
<span class="ml-2 hidden xl:block">Application</span>
|
||||||
|
</x-tab-item>
|
||||||
|
@if ($site->hasFeature(SiteFeature::SSL))
|
||||||
|
<x-tab-item
|
||||||
|
class="mr-1"
|
||||||
|
:href="route('servers.sites.ssl', ['server' => $site->server, 'site' => $site])"
|
||||||
|
:active="request()->routeIs('servers.sites.ssl')"
|
||||||
|
>
|
||||||
|
<x-heroicon name="o-lock-closed" class="h-5 w-5" />
|
||||||
|
<span class="ml-2 hidden xl:block">SSL</span>
|
||||||
|
</x-tab-item>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
@if ($site->hasFeature(SiteFeature::QUEUES))
|
||||||
|
<x-tab-item
|
||||||
|
class="mr-1"
|
||||||
|
:href="route('servers.sites.queues', ['server' => $site->server, 'site' => $site])"
|
||||||
|
:active="request()->routeIs('servers.sites.queues')"
|
||||||
|
>
|
||||||
|
<x-heroicon name="o-queue-list" class="h-5 w-5" />
|
||||||
|
<span class="ml-2 hidden xl:block">Queues</span>
|
||||||
|
</x-tab-item>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
<x-tab-item
|
||||||
|
class="mr-1"
|
||||||
|
:href="route('servers.sites.settings', ['server' => $site->server, 'site' => $site])"
|
||||||
|
:active="request()->routeIs('servers.sites.settings')"
|
||||||
|
>
|
||||||
|
<x-heroicon name="o-cog-6-tooth" class="h-5 w-5" />
|
||||||
|
<span class="ml-2 hidden xl:block">Settings</span>
|
||||||
|
</x-tab-item>
|
||||||
|
<x-tab-item
|
||||||
|
class="mr-1"
|
||||||
|
:href="route('servers.sites.logs', ['server' => $site->server, 'site' => $site])"
|
||||||
|
:active="request()->routeIs('servers.sites.logs')"
|
||||||
|
>
|
||||||
|
<x-heroicon name="o-square-3-stack-3d" class="h-5 w-5" />
|
||||||
|
<span class="ml-2 hidden xl:block">Logs</span>
|
||||||
|
</x-tab-item>
|
||||||
|
</div>
|
||||||
|
<div class="md:hidden">
|
||||||
|
<x-dropdown align="left">
|
||||||
|
<x-slot name="trigger">
|
||||||
|
<div
|
||||||
|
class="flex w-full cursor-pointer items-center rounded-md border border-gray-300 bg-gray-50 p-2.5 text-sm text-gray-900 focus:border-primary-500 focus:ring-primary-500 dark:border-gray-600 dark:bg-gray-700 dark:text-white dark:placeholder-gray-400 dark:focus:border-primary-500 dark:focus:ring-primary-500"
|
||||||
|
>
|
||||||
|
Select
|
||||||
|
<button type="button" class="ml-2">
|
||||||
|
<x-heroicon name="o-chevron-down" class="h-4 w-4 text-gray-400" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</x-slot>
|
||||||
|
<x-slot name="content">
|
||||||
|
<x-dropdown-link
|
||||||
|
:href="route('servers.sites.show', ['server' => $site->server, 'site' => $site])"
|
||||||
|
:active="request()->routeIs('servers.sites.show')"
|
||||||
|
>
|
||||||
|
<x-heroicon name="o-globe-alt" class="h-5 w-5" />
|
||||||
|
<span class="ml-2">Application</span>
|
||||||
|
</x-dropdown-link>
|
||||||
|
@if ($site->hasFeature(SiteFeature::SSL))
|
||||||
|
<x-dropdown-link
|
||||||
|
:href="route('servers.sites.ssl', ['server' => $site->server, 'site' => $site])"
|
||||||
|
:active="request()->routeIs('servers.sites.ssl')"
|
||||||
|
>
|
||||||
|
<x-heroicon name="o-lock-closed" class="h-5 w-5" />
|
||||||
|
<span class="ml-2">SSL</span>
|
||||||
|
</x-dropdown-link>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
@if ($site->hasFeature(SiteFeature::QUEUES))
|
||||||
|
<x-dropdown-link
|
||||||
|
:href="route('servers.sites.queues', ['server' => $site->server, 'site' => $site])"
|
||||||
|
:active="request()->routeIs('servers.sites.queues')"
|
||||||
|
>
|
||||||
|
<x-heroicon name="o-queue-list" class="h-5 w-5" />
|
||||||
|
<span class="ml-2">Queues</span>
|
||||||
|
</x-dropdown-link>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
<x-dropdown-link
|
||||||
|
:href="route('servers.sites.settings', ['server' => $site->server, 'site' => $site])"
|
||||||
|
:active="request()->routeIs('servers.sites.settings')"
|
||||||
|
>
|
||||||
|
<x-heroicon name="o-cog-6-tooth" class="h-5 w-5" />
|
||||||
|
<span class="ml-2">Settings</span>
|
||||||
|
</x-dropdown-link>
|
||||||
|
<x-dropdown-link
|
||||||
|
:href="route('servers.sites.logs', ['server' => $site->server, 'site' => $site])"
|
||||||
|
:active="request()->routeIs('servers.sites.logs')"
|
||||||
|
>
|
||||||
|
<x-heroicon name="o-square-3-stack-3d" class="h-5 w-5" />
|
||||||
|
<span class="ml-2">Logs</span>
|
||||||
|
</x-dropdown-link>
|
||||||
|
</x-slot>
|
||||||
|
</x-dropdown>
|
||||||
|
</div>
|
||||||
<div class="flex items-end">
|
<div class="flex items-end">
|
||||||
<div class="flex h-20 flex-col items-end justify-center">
|
<div class="flex h-20 flex-col items-end justify-center">
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
@ -70,57 +170,6 @@ class="h-4 w-4 font-bold text-primary-600 dark:text-white"
|
|||||||
</div>
|
</div>
|
||||||
</x-slot>
|
</x-slot>
|
||||||
|
|
||||||
<x-slot name="sidebar">
|
|
||||||
<div class="flex h-16 items-center justify-center border-b border-gray-200 px-3 py-2 dark:border-gray-800">
|
|
||||||
<div class="w-full">
|
|
||||||
@include("layouts.partials.site-select", ["server" => $site->server, "site" => $site])
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="space-y-2 p-3">
|
|
||||||
<x-secondary-sidebar-link
|
|
||||||
:href="route('servers.sites.show', ['server' => $site->server, 'site' => $site])"
|
|
||||||
:active="request()->routeIs('servers.sites.show')"
|
|
||||||
>
|
|
||||||
<x-heroicon name="o-globe-alt" class="mr-2 h-5 w-5" />
|
|
||||||
{{ __("Application") }}
|
|
||||||
</x-secondary-sidebar-link>
|
|
||||||
@if ($site->hasFeature(SiteFeature::SSL))
|
|
||||||
<x-secondary-sidebar-link
|
|
||||||
:href="route('servers.sites.ssl', ['server' => $site->server, 'site' => $site])"
|
|
||||||
:active="request()->routeIs('servers.sites.ssl')"
|
|
||||||
>
|
|
||||||
<x-heroicon name="o-lock-closed" class="mr-2 h-5 w-5" />
|
|
||||||
{{ __("SSL") }}
|
|
||||||
</x-secondary-sidebar-link>
|
|
||||||
@endif
|
|
||||||
|
|
||||||
@if ($site->hasFeature(SiteFeature::QUEUES))
|
|
||||||
<x-secondary-sidebar-link
|
|
||||||
:href="route('servers.sites.queues', ['server' => $site->server, 'site' => $site])"
|
|
||||||
:active="request()->routeIs('servers.sites.queues')"
|
|
||||||
>
|
|
||||||
<x-heroicon name="o-queue-list" class="mr-2 h-5 w-5" />
|
|
||||||
{{ __("Queues") }}
|
|
||||||
</x-secondary-sidebar-link>
|
|
||||||
@endif
|
|
||||||
|
|
||||||
<x-secondary-sidebar-link
|
|
||||||
:href="route('servers.sites.settings', ['server' => $site->server, 'site' => $site])"
|
|
||||||
:active="request()->routeIs('servers.sites.settings')"
|
|
||||||
>
|
|
||||||
<x-heroicon name="o-cog-6-tooth" class="mr-2 h-5 w-5" />
|
|
||||||
{{ __("Settings") }}
|
|
||||||
</x-secondary-sidebar-link>
|
|
||||||
<x-secondary-sidebar-link
|
|
||||||
:href="route('servers.sites.logs', ['server' => $site->server, 'site' => $site])"
|
|
||||||
:active="request()->routeIs('servers.sites.logs')"
|
|
||||||
>
|
|
||||||
<x-heroicon name="o-square-3-stack-3d" class="mr-2 h-5 w-5" />
|
|
||||||
{{ __("Logs") }}
|
|
||||||
</x-secondary-sidebar-link>
|
|
||||||
</div>
|
|
||||||
</x-slot>
|
|
||||||
|
|
||||||
<x-container class="flex">
|
<x-container class="flex">
|
||||||
<div class="w-full space-y-10">
|
<div class="w-full space-y-10">
|
||||||
{{ $slot }}
|
{{ $slot }}
|
||||||
|
@ -18,13 +18,13 @@
|
|||||||
</x-card-header>
|
</x-card-header>
|
||||||
<x-live id="live-server-logs">
|
<x-live id="live-server-logs">
|
||||||
<x-table>
|
<x-table>
|
||||||
<tr>
|
<x-tr>
|
||||||
<x-th>{{ __("Event") }}</x-th>
|
<x-th>{{ __("Event") }}</x-th>
|
||||||
<x-th>{{ __("Date") }}</x-th>
|
<x-th>{{ __("Date") }}</x-th>
|
||||||
<x-th></x-th>
|
<x-th></x-th>
|
||||||
</tr>
|
</x-tr>
|
||||||
@foreach ($logs as $log)
|
@foreach ($logs as $log)
|
||||||
<tr>
|
<x-tr>
|
||||||
<x-td>{{ $log->type }}</x-td>
|
<x-td>{{ $log->type }}</x-td>
|
||||||
<x-td>
|
<x-td>
|
||||||
<x-datetime :value="$log->created_at" />
|
<x-datetime :value="$log->created_at" />
|
||||||
@ -39,7 +39,7 @@
|
|||||||
<x-heroicon name="o-eye" class="h-5 w-5" />
|
<x-heroicon name="o-eye" class="h-5 w-5" />
|
||||||
</x-icon-button>
|
</x-icon-button>
|
||||||
</x-td>
|
</x-td>
|
||||||
</tr>
|
</x-tr>
|
||||||
@endforeach
|
@endforeach
|
||||||
</x-table>
|
</x-table>
|
||||||
@if ($logs instanceof \Illuminate\Pagination\LengthAwarePaginator)
|
@if ($logs instanceof \Illuminate\Pagination\LengthAwarePaginator)
|
||||||
|
@ -5,11 +5,11 @@
|
|||||||
{{ __("Add or modify your ssh keys") }}
|
{{ __("Add or modify your ssh keys") }}
|
||||||
</x-slot>
|
</x-slot>
|
||||||
<x-slot name="aside">
|
<x-slot name="aside">
|
||||||
<div class="flex items-center">
|
<div class="flex flex-col items-end lg:flex-row lg:items-center">
|
||||||
<div>
|
<div class="mb-2 lg:mb-0 lg:mr-2">
|
||||||
@include("server-ssh-keys.partials.add-new-key")
|
@include("server-ssh-keys.partials.add-new-key")
|
||||||
</div>
|
</div>
|
||||||
<div class="ml-2">
|
<div>
|
||||||
@include("server-ssh-keys.partials.add-existing-key")
|
@include("server-ssh-keys.partials.add-existing-key")
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -12,14 +12,14 @@
|
|||||||
<div x-data="" class="space-y-3">
|
<div x-data="" class="space-y-3">
|
||||||
@if (count($ssls) > 0)
|
@if (count($ssls) > 0)
|
||||||
<x-table>
|
<x-table>
|
||||||
<tr>
|
<x-tr>
|
||||||
<x-th>{{ __("Type") }}</x-th>
|
<x-th>{{ __("Type") }}</x-th>
|
||||||
<x-th>{{ __("Created") }}</x-th>
|
<x-th>{{ __("Created") }}</x-th>
|
||||||
<x-th>{{ __("Expires at") }}</x-th>
|
<x-th>{{ __("Expires at") }}</x-th>
|
||||||
<x-th></x-th>
|
<x-th></x-th>
|
||||||
</tr>
|
</x-tr>
|
||||||
@foreach ($ssls as $ssl)
|
@foreach ($ssls as $ssl)
|
||||||
<tr>
|
<x-tr>
|
||||||
<x-td>{{ $ssl->type }}</x-td>
|
<x-td>{{ $ssl->type }}</x-td>
|
||||||
<x-td>
|
<x-td>
|
||||||
<x-datetime :value="$ssl->created_at" />
|
<x-datetime :value="$ssl->created_at" />
|
||||||
@ -39,7 +39,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</x-td>
|
</x-td>
|
||||||
</tr>
|
</x-tr>
|
||||||
@endforeach
|
@endforeach
|
||||||
</x-table>
|
</x-table>
|
||||||
@else
|
@else
|
||||||
|
@ -22,10 +22,12 @@
|
|||||||
Route::get('/', [ServerController::class, 'index'])->name('servers');
|
Route::get('/', [ServerController::class, 'index'])->name('servers');
|
||||||
Route::get('/create', [ServerController::class, 'create'])->name('servers.create');
|
Route::get('/create', [ServerController::class, 'create'])->name('servers.create');
|
||||||
Route::post('/create', [ServerController::class, 'store'])->name('servers.create');
|
Route::post('/create', [ServerController::class, 'store'])->name('servers.create');
|
||||||
Route::get('/{server}', [ServerController::class, 'show'])->name('servers.show');
|
|
||||||
Route::delete('/{server}', [ServerController::class, 'delete'])->name('servers.delete');
|
|
||||||
|
|
||||||
Route::middleware(['server-is-ready', 'handle-ssh-errors'])->group(function () {
|
Route::middleware('select-current-project')->group(function () {
|
||||||
|
Route::get('/{server}', [ServerController::class, 'show'])->name('servers.show');
|
||||||
|
Route::delete('/{server}', [ServerController::class, 'delete'])->name('servers.delete');
|
||||||
|
|
||||||
|
Route::middleware(['server-is-ready', 'handle-ssh-errors'])->group(function () {
|
||||||
Route::prefix('/{server}/sites')->group(function () {
|
Route::prefix('/{server}/sites')->group(function () {
|
||||||
// sites
|
// sites
|
||||||
Route::get('/', [SiteController::class, 'index'])->name('servers.sites');
|
Route::get('/', [SiteController::class, 'index'])->name('servers.sites');
|
||||||
@ -121,18 +123,19 @@
|
|||||||
Route::get('/{server}/services/{service}/restart', [ServiceController::class, 'restart'])->name('servers.services.restart');
|
Route::get('/{server}/services/{service}/restart', [ServiceController::class, 'restart'])->name('servers.services.restart');
|
||||||
Route::get('/{server}/services/{service}/enable', [ServiceController::class, 'enable'])->name('servers.services.enable');
|
Route::get('/{server}/services/{service}/enable', [ServiceController::class, 'enable'])->name('servers.services.enable');
|
||||||
Route::get('/{server}/services/{service}/disable', [ServiceController::class, 'disable'])->name('servers.services.disable');
|
Route::get('/{server}/services/{service}/disable', [ServiceController::class, 'disable'])->name('servers.services.disable');
|
||||||
});
|
});
|
||||||
|
|
||||||
// settings
|
// settings
|
||||||
Route::prefix('/{server}/settings')->group(function () {
|
Route::prefix('/{server}/settings')->group(function () {
|
||||||
Route::get('/', [ServerSettingController::class, 'index'])->name('servers.settings');
|
Route::get('/', [ServerSettingController::class, 'index'])->name('servers.settings');
|
||||||
Route::post('/check-connection', [ServerSettingController::class, 'checkConnection'])->name('servers.settings.check-connection');
|
Route::post('/check-connection', [ServerSettingController::class, 'checkConnection'])->name('servers.settings.check-connection');
|
||||||
Route::post('/reboot', [ServerSettingController::class, 'reboot'])->name('servers.settings.reboot');
|
Route::post('/reboot', [ServerSettingController::class, 'reboot'])->name('servers.settings.reboot');
|
||||||
Route::post('/edit', [ServerSettingController::class, 'edit'])->name('servers.settings.edit');
|
Route::post('/edit', [ServerSettingController::class, 'edit'])->name('servers.settings.edit');
|
||||||
});
|
});
|
||||||
|
|
||||||
// logs
|
// logs
|
||||||
Route::prefix('/{server}/logs')->group(function () {
|
Route::prefix('/{server}/logs')->group(function () {
|
||||||
Route::get('/', [ServerLogController::class, 'index'])->name('servers.logs');
|
Route::get('/', [ServerLogController::class, 'index'])->name('servers.logs');
|
||||||
Route::get('/{serverLog}', [ServerLogController::class, 'show'])->name('servers.logs.show');
|
Route::get('/{serverLog}', [ServerLogController::class, 'show'])->name('servers.logs.show');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
use App\Http\Controllers\SearchController;
|
||||||
use Illuminate\Support\Facades\Route;
|
use Illuminate\Support\Facades\Route;
|
||||||
|
|
||||||
Route::get('/', function () {
|
Route::get('/', function () {
|
||||||
@ -12,4 +13,6 @@
|
|||||||
Route::prefix('/servers')->group(function () {
|
Route::prefix('/servers')->group(function () {
|
||||||
require __DIR__.'/server.php';
|
require __DIR__.'/server.php';
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Route::get('/search', [SearchController::class, 'search'])->name('search');
|
||||||
});
|
});
|
||||||
|
@ -1,14 +1,17 @@
|
|||||||
const defaultTheme = require('tailwindcss/defaultTheme');
|
import defaultTheme from 'tailwindcss/defaultTheme';
|
||||||
const colors = require("tailwindcss/colors");
|
import forms from '@tailwindcss/forms';
|
||||||
|
import colors from "tailwindcss/colors";
|
||||||
|
import flowbite from 'flowbite/plugin';
|
||||||
|
|
||||||
/** @type {import('tailwindcss').Config} */
|
/** @type {import('tailwindcss').Config} */
|
||||||
module.exports = {
|
export default {
|
||||||
darkMode: 'class',
|
darkMode: 'class',
|
||||||
|
|
||||||
content: [
|
content: [
|
||||||
'./vendor/laravel/framework/src/Illuminate/Pagination/resources/views/*.blade.php',
|
'./vendor/laravel/framework/src/Illuminate/Pagination/resources/views/*.blade.php',
|
||||||
'./storage/framework/views/*.php',
|
'./storage/framework/views/*.php',
|
||||||
'./resources/views/**/*.blade.php',
|
'./resources/views/**/*.blade.php',
|
||||||
'./resources/js/**/*.vue',
|
"./node_modules/flowbite/**/*.js"
|
||||||
],
|
],
|
||||||
|
|
||||||
theme: {
|
theme: {
|
||||||
@ -16,16 +19,23 @@ module.exports = {
|
|||||||
fontFamily: {
|
fontFamily: {
|
||||||
sans: ['Figtree', ...defaultTheme.fontFamily.sans],
|
sans: ['Figtree', ...defaultTheme.fontFamily.sans],
|
||||||
},
|
},
|
||||||
|
|
||||||
colors: {
|
colors: {
|
||||||
gray: colors.slate,
|
gray: colors.slate,
|
||||||
primary: colors.indigo
|
primary: colors.indigo,
|
||||||
|
green: colors.emerald,
|
||||||
|
},
|
||||||
|
variants: {
|
||||||
|
extend: {
|
||||||
|
border: ['last'],
|
||||||
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
plugins: [
|
plugins: [
|
||||||
require('@tailwindcss/forms'),
|
forms,
|
||||||
require('@tailwindcss/typography'),
|
flowbite({
|
||||||
|
charts: true
|
||||||
|
})
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
58
tests/Feature/SearchTest.php
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Tests\Feature;
|
||||||
|
|
||||||
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
|
use Tests\TestCase;
|
||||||
|
|
||||||
|
class SearchTest extends TestCase
|
||||||
|
{
|
||||||
|
use RefreshDatabase;
|
||||||
|
|
||||||
|
public function test_search_server(): void
|
||||||
|
{
|
||||||
|
$this->actingAs($this->user);
|
||||||
|
|
||||||
|
$this->get(route('search', ['q' => $this->server->name]))
|
||||||
|
->assertOk()
|
||||||
|
->assertJson([
|
||||||
|
'results' => [
|
||||||
|
[
|
||||||
|
'type' => 'server',
|
||||||
|
'url' => route('servers.show', ['server' => $this->site->server]),
|
||||||
|
'text' => $this->server->name,
|
||||||
|
'project' => $this->server->project->name,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_search_site(): void
|
||||||
|
{
|
||||||
|
$this->actingAs($this->user);
|
||||||
|
|
||||||
|
$this->get(route('search', ['q' => $this->site->domain]))
|
||||||
|
->assertOk()
|
||||||
|
->assertJson([
|
||||||
|
'results' => [
|
||||||
|
[
|
||||||
|
'type' => 'site',
|
||||||
|
'url' => route('servers.sites.show', ['server' => $this->site->server, 'site' => $this->site]),
|
||||||
|
'text' => $this->site->domain,
|
||||||
|
'project' => $this->site->server->project->name,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_search_has_no_results(): void
|
||||||
|
{
|
||||||
|
$this->actingAs($this->user);
|
||||||
|
|
||||||
|
$this->get(route('search', ['q' => 'nothing-will-found']))
|
||||||
|
->assertOk()
|
||||||
|
->assertJson([
|
||||||
|
'results' => [],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
@ -31,6 +31,7 @@ public function setUp(): void
|
|||||||
config()->set('filesystems.disks.key-pairs.root', storage_path('app/key-pairs-test'));
|
config()->set('filesystems.disks.key-pairs.root', storage_path('app/key-pairs-test'));
|
||||||
|
|
||||||
$this->user = User::factory()->create();
|
$this->user = User::factory()->create();
|
||||||
|
$this->user->createDefaultProject();
|
||||||
|
|
||||||
\App\Models\NotificationChannel::factory()->create([
|
\App\Models\NotificationChannel::factory()->create([
|
||||||
'provider' => NotificationChannel::EMAIL,
|
'provider' => NotificationChannel::EMAIL,
|
||||||
@ -60,6 +61,7 @@ private function setupServer(): void
|
|||||||
{
|
{
|
||||||
$this->server = Server::factory()->create([
|
$this->server = Server::factory()->create([
|
||||||
'user_id' => $this->user->id,
|
'user_id' => $this->user->id,
|
||||||
|
'project_id' => $this->user->current_project_id,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$keys = $this->server->sshKey();
|
$keys = $this->server->sshKey();
|
||||||
|