Skip to content

Commit 7dbdcef

Browse files
authored
Merge pull request #870 from hydephp/realtime-compiler-dashboard
Add a dashboard feature to the realtime compiler
2 parents eb20d77 + d88863f commit 7dbdcef

File tree

6 files changed

+295
-1
lines changed

6 files changed

+295
-1
lines changed

.github/workflows/end-to-end-testing.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ jobs:
3030
run: vendor/bin/pest --stop-on-failure
3131

3232
- name: Prepare the Environment
33-
run: echo -e "APP_URL=http://localhost:8080 \nDUSK_ENABLED=true" > .env
33+
run: echo -e "APP_URL=http://localhost:8080 \nDUSK_ENABLED=true\nSERVER_DASHBOARD=false" > .env
3434

3535
- name: Upgrade Chrome Driver
3636
run: php hyde dusk:chrome-driver `/opt/google/chrome/chrome --version | cut -d " " -f3 | cut -d "." -f1`

config/hyde.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828

2929
'server' => [
3030
'port' => env('SERVER_PORT', 8080),
31+
'dashboard' => env('SERVER_DASHBOARD', true),
3132
],
3233

3334
/*

packages/framework/config/hyde.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828

2929
'server' => [
3030
'port' => env('SERVER_PORT', 8080),
31+
'dashboard' => env('SERVER_DASHBOARD', true),
3132
],
3233

3334
/*
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
@php /** @var \Hyde\RealtimeCompiler\Http\DashboardController $dashboard */ @endphp
2+
<!doctype html>
3+
<html lang="en">
4+
<head>
5+
<meta charset="UTF-8">
6+
<meta name="viewport" content="width=device-width, initial-scale=1">
7+
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-Zenh87qX5JnK2Jl0vWa8Ck2rdkQ2Bzep5IDxbcnCeuOxjzrPF/et3URy9Bv1WTRi" crossorigin="anonymous">
8+
<title>{{ $title }}</title>
9+
<base target="_parent">
10+
</head>
11+
<body class="d-flex flex-column min-vh-100">
12+
<nav class="navbar navbar-dark bg-dark flex-md-nowrap p-2">
13+
<a class="navbar-brand col-md-3 col-lg-2 me-0 px-3 fs-6" href="/dashboard" style="font-weight: 600;">{{ $title }}</a>
14+
<div class="navbar-nav">
15+
@if($request->embedded)
16+
<div class="nav-item text-nowrap pe-4">
17+
<a class="nav-link px-3" href="/dashboard">Open full page dashboard</a>
18+
</div>
19+
@else
20+
<div class="nav-item text-nowrap">
21+
<a class="nav-link px-3" href="/">Back to site</a>
22+
</div>
23+
@endif
24+
</div>
25+
</nav>
26+
<main class="container py-4 mb-auto">
27+
<div class="col-xl-10 mx-auto">
28+
<header class="px-4 py-5 my-4 text-center bg-light">
29+
<h1 class="display-6 fw-bold">{{ $title }}</h1>
30+
<div class="mx-auto">
31+
<h2 class="h4">Welcome to the dashboard for your HydePHP site.</h2>
32+
<p class="lead mb-0">This page is accessible through the Hyde Realtime Compiler and will not be saved to your static site.</p>
33+
</div>
34+
</header>
35+
</div>
36+
<section>
37+
<div class="col-xl-10 mx-auto">
38+
<div class="card mb-4">
39+
<div class="card-header">
40+
<h2 class="h5 mb-0">Project Information</h2>
41+
</div>
42+
<div class="card-body">
43+
<table class="table table-bordered">
44+
<tr>
45+
@foreach($dashboard->getProjectInformation() as $type => $info)
46+
<td>
47+
<strong class="h6">{{ $type }}</strong>
48+
<span class="card-text">{{ $info }}</span>
49+
</td>
50+
@endforeach
51+
</tr>
52+
</table>
53+
</div>
54+
</div>
55+
</div>
56+
</section>
57+
<section>
58+
<div class="col-xl-10 mx-auto">
59+
<div class="card mb-4">
60+
<div class="card-header">
61+
<h2 class="h5 mb-0">Site Pages & Routes</h2>
62+
</div>
63+
<div class="card-body">
64+
<table class="table table-bordered">
65+
<tr>
66+
@foreach(['Page Type', 'Route Key', 'Source File', 'Output File', 'Identifier'] as $header)
67+
<th>{{ $header }}</th>
68+
@endforeach
69+
<th class="text-end">Actions</th>
70+
</tr>
71+
@foreach($dashboard->getPageList() as $route)
72+
<tr>
73+
<td>
74+
<code title="\{{ $route->getPageClass() }}">{{ class_basename($route->getPageClass()) }}</code>
75+
</td>
76+
<td>
77+
{{ $route->getRouteKey() }}
78+
</td>
79+
<td>
80+
{{ $route->getSourcePath() }}
81+
</td>
82+
<td>
83+
{{ $route->getOutputPath() }}
84+
</td>
85+
<td>
86+
{{ $route->getPageIdentifier() }}
87+
</td>
88+
<td class="text-end">
89+
<a href="{{ $route->getLink() }}" class="btn btn-outline-primary btn-sm">View</a>
90+
</td>
91+
</tr>
92+
@endforeach
93+
</table>
94+
</div>
95+
</div>
96+
</div>
97+
</section>
98+
</main>
99+
<footer class="bg-light text-center py-3 mt-3">
100+
<div class="container d-flex align-items-center justify-content-between">
101+
<div class="col-lg-3"></div>
102+
<div class="col-lg-6">
103+
<p class="mb-1">
104+
HydePHP Realtime Compiler <span class="text-muted">{{ $dashboard->getVersion() }}</span>
105+
</p>
106+
</div>
107+
<div class="col-lg-3"></div>
108+
</div>
109+
</footer>
110+
</body>
111+
</html>
Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Hyde\RealtimeCompiler\Http;
6+
7+
use function app;
8+
use function array_merge;
9+
use Composer\InstalledVersions;
10+
use function config;
11+
use Desilva\Microserve\Request;
12+
use function file_get_contents;
13+
use Hyde\Framework\Actions\AnonymousViewCompiler;
14+
use Hyde\Framework\Actions\StaticPageBuilder;
15+
use Hyde\Hyde;
16+
use Hyde\Pages\Concerns\HydePage;
17+
use function sprintf;
18+
use function str_replace;
19+
use function str_starts_with;
20+
21+
/**
22+
* @internal
23+
* @experimental
24+
*/
25+
class DashboardController
26+
{
27+
public string $title;
28+
29+
public function __construct()
30+
{
31+
$this->title = config('site.name').' - Dashboard';
32+
}
33+
34+
public function show(): string
35+
{
36+
return AnonymousViewCompiler::call(__DIR__.'/../../resources/dashboard.blade.php', array_merge(
37+
(array) $this, ['dashboard' => $this, 'request' => Request::capture()],
38+
));
39+
}
40+
41+
public function getVersion(): string
42+
{
43+
$version = InstalledVersions::getPrettyVersion('hyde/realtime-compiler');
44+
45+
return str_starts_with($version, 'dev-') ? $version : "v$version";
46+
}
47+
48+
public function getProjectInformation(): array
49+
{
50+
return [
51+
'Git Version:' => app('git.version'),
52+
'Hyde Version:' => InstalledVersions::getPrettyVersion('hyde/hyde') ?: 'unreleased',
53+
'Framework Version:' => InstalledVersions::getPrettyVersion('hyde/framework') ?: 'unreleased',
54+
'Project Path:' => Hyde::path(),
55+
];
56+
}
57+
58+
/** @return array<string, \Hyde\Support\Models\Route> */
59+
public function getPageList(): array
60+
{
61+
return Hyde::routes()->all();
62+
}
63+
64+
public static function enabled(): bool
65+
{
66+
return config('hyde.server.dashboard', true);
67+
}
68+
69+
// This method is called from the PageRouter and allows us to serve a dynamic welcome page
70+
public static function renderIndexPage(HydePage $page): string
71+
{
72+
$contents = file_get_contents((new StaticPageBuilder($page))->__invoke());
73+
74+
// If the page is the default welcome page we inject dashboard components
75+
if (str_contains($contents, 'This is the default homepage')) {
76+
if (config('hyde.server.dashboard.welcome-banner', true)) {
77+
$contents = str_replace("</div>\n <!-- End Main Hero Content -->",
78+
sprintf("%s\n</div>\n<!-- End Main Hero Content -->", self::welcomeComponent()),
79+
$contents);
80+
}
81+
82+
if (config('hyde.server.dashboard.welcome-dashboard', true)) {
83+
$contents = str_replace('</body>', sprintf("%s\n</body>", self::welcomeFrame()), $contents);
84+
}
85+
86+
if (config('hyde.server.dashboard.button', false)) {
87+
$contents = self::injectDashboardButton($contents);
88+
}
89+
}
90+
91+
return $contents;
92+
}
93+
94+
protected static function injectDashboardButton(string $contents): string
95+
{
96+
return str_replace('</body>', sprintf('%s</body>', self::button()), $contents);
97+
}
98+
99+
protected static function button(): string
100+
{
101+
return <<<'HTML'
102+
<style>
103+
.dashboard-btn {
104+
background-image: linear-gradient(to right, #1FA2FF 0%, #12D8FA 51%, #1FA2FF 100%);
105+
margin: 10px;
106+
padding: .5rem 1rem;
107+
text-align: center;
108+
transition: 0.5s;
109+
background-size: 200% auto;
110+
background-position: right center;
111+
color: white;
112+
box-shadow: 0 0 20px #162134;
113+
border-radius: 10px;
114+
display: block;
115+
position: absolute;
116+
right: 1rem;
117+
top: 1rem
118+
}
119+
120+
.dashboard-btn:hover {
121+
background-position: left center;
122+
color: #fff;
123+
text-decoration: none;
124+
}
125+
</style>
126+
<a href="/dashboard" class="dashboard-btn">Dashboard</a>
127+
HTML;
128+
}
129+
130+
protected static function welcomeComponent(): string
131+
{
132+
$dashboardMessage = config('hyde.server.dashboard.welcome-dashboard', true)
133+
? '<br>Scroll down to see it, or visit <a href="/dashboard" style="color: #1FA2FF;">/dashboard</a> at any time!' : '';
134+
135+
return <<<HTML
136+
<!-- Dashboard Component -->
137+
<section class="text-white">
138+
<hr style="border-width: 1px; max-width: 240px; opacity: .75; margin-top: 30px; margin-bottom: 24px">
139+
<p style="margin-bottom: 8px;">
140+
<span style="
141+
background: #1FA2FF;
142+
background: -webkit-linear-gradient(to right, #1FA2FF, #12D8FA, #1FA2FF);
143+
background: linear-gradient(to right, #1FA2FF, #12D8FA, #1FA2FF);
144+
padding: 3px 8px;
145+
border-radius: 25px;
146+
font-size: 12px;
147+
text-transform: uppercase;
148+
font-weight: 600;
149+
">New</span> When using the Realtime Compiler, you now have a content dashboard!
150+
$dashboardMessage
151+
</p>
152+
153+
<a href="#dashboard" onclick="document.getElementById('dashboard').scrollIntoView({behavior: 'smooth'}); return false;">
154+
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#ffffff"><path d="M0 0h24v24H0z" fill="none"/><path d="M16.59 8.59L12 13.17 7.41 8.59 6 10l6 6 6-6z"/></svg>
155+
</a>
156+
</section>
157+
<!-- End Dashboard Component -->
158+
HTML;
159+
}
160+
161+
protected static function welcomeFrame(): string
162+
{
163+
return <<<'HTML'
164+
<aside>
165+
<iframe id="dashboard" src="/dashboard?embedded=true" frameborder="0" style="width: 100vw; height: 100vh; position: absolute;"></iframe>
166+
</aside>
167+
HTML;
168+
}
169+
}

packages/realtime-compiler/src/Routing/PageRouter.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
use Hyde\Pages\Concerns\HydePage;
99
use Hyde\RealtimeCompiler\Concerns\InteractsWithLaravel;
1010
use Hyde\RealtimeCompiler\Concerns\SendsErrorResponses;
11+
use Hyde\RealtimeCompiler\Http\DashboardController;
12+
use Hyde\RealtimeCompiler\Http\HtmlResponse;
1113
use Hyde\Support\Models\Route;
1214

1315
/**
@@ -28,6 +30,12 @@ public function __construct(Request $request)
2830

2931
protected function handlePageRequest(): Response
3032
{
33+
if ($this->request->path === '/dashboard' && DashboardController::enabled()) {
34+
return new HtmlResponse(200, 'OK', [
35+
'body' => (new DashboardController())->show(),
36+
]);
37+
}
38+
3139
$html = $this->getHtml(Route::getOrFail($this->normalizePath($this->request->path))->getPage());
3240

3341
return (new Response(200, 'OK', [
@@ -55,6 +63,10 @@ protected function normalizePath(string $path): string
5563

5664
protected function getHtml(HydePage $page): string
5765
{
66+
if ($page->identifier === 'index' && DashboardController::enabled()) {
67+
return DashboardController::renderIndexPage($page);
68+
}
69+
5870
return file_get_contents((new StaticPageBuilder($page))->__invoke());
5971
}
6072

0 commit comments

Comments
 (0)