Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c6de984145 | |||
|
9e9fbdc127 |
12 changed files with 252 additions and 0 deletions
16
app/Http/Controllers/AboutPageController.php
Normal file
16
app/Http/Controllers/AboutPageController.php
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use App\Models\About;
|
||||||
|
use Illuminate\View\View;
|
||||||
|
|
||||||
|
class AboutPageController extends Controller
|
||||||
|
{
|
||||||
|
public function show(): View
|
||||||
|
{
|
||||||
|
return view('about', [
|
||||||
|
'about' => About::first()?->content,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
32
app/Http/Controllers/Admin/AboutController.php
Normal file
32
app/Http/Controllers/Admin/AboutController.php
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Admin;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Models\About;
|
||||||
|
use Illuminate\Http\RedirectResponse;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\View\View;
|
||||||
|
|
||||||
|
class AboutController extends Controller
|
||||||
|
{
|
||||||
|
public function show(): View
|
||||||
|
{
|
||||||
|
$about = About::first();
|
||||||
|
|
||||||
|
return view('admin.about.show', [
|
||||||
|
'aboutEntry' => $about,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function update(Request $request): RedirectResponse
|
||||||
|
{
|
||||||
|
$about = About::firstOrNew();
|
||||||
|
$about->content = $request->input('content');
|
||||||
|
$about->save();
|
||||||
|
|
||||||
|
return redirect()->route('admin.about.show');
|
||||||
|
}
|
||||||
|
}
|
||||||
13
app/Models/About.php
Normal file
13
app/Models/About.php
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
|
class About extends Model
|
||||||
|
{
|
||||||
|
use HasFactory;
|
||||||
|
|
||||||
|
protected $table = 'about';
|
||||||
|
}
|
||||||
24
database/factories/AboutFactory.php
Normal file
24
database/factories/AboutFactory.php
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Database\Factories;
|
||||||
|
|
||||||
|
use App\Models\About;
|
||||||
|
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @extends Factory<About>
|
||||||
|
*/
|
||||||
|
class AboutFactory extends Factory
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Define the model's default state.
|
||||||
|
*
|
||||||
|
* @return array<string, mixed>
|
||||||
|
*/
|
||||||
|
public function definition(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'content' => $this->faker->paragraph,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
28
database/migrations/2026_08_28_142830_create_about_table.php
Normal file
28
database/migrations/2026_08_28_142830_create_about_table.php
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::create('about', function (Blueprint $table) {
|
||||||
|
$table->id();
|
||||||
|
$table->text('content');
|
||||||
|
$table->timestamps();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('about');
|
||||||
|
}
|
||||||
|
};
|
||||||
9
resources/views/about.blade.php
Normal file
9
resources/views/about.blade.php
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
@extends('master')
|
||||||
|
@section('title')About « @stop
|
||||||
|
|
||||||
|
@section('content')
|
||||||
|
<h2>About</h2>
|
||||||
|
<div class="e-content">
|
||||||
|
{!! $about !!}
|
||||||
|
</div>
|
||||||
|
@stop
|
||||||
19
resources/views/admin/about/show.blade.php
Normal file
19
resources/views/admin/about/show.blade.php
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
@extends('master')
|
||||||
|
|
||||||
|
@section('title')Edit About « Admin CP « @stop
|
||||||
|
|
||||||
|
@section('content')
|
||||||
|
<h1>Edit About</h1>
|
||||||
|
<form action="/admin/about" method="post" accept-charset="utf-8" class="admin-form form">
|
||||||
|
{{ csrf_field() }}
|
||||||
|
{{ method_field('PUT') }}
|
||||||
|
<div>
|
||||||
|
<label for="content">Content:</label>
|
||||||
|
<br>
|
||||||
|
<textarea name="content" id="content" rows="10" cols="50">{{ old('content', $aboutEntry?->content) }}</textarea>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<button type="submit" name="save">Save</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
@stop
|
||||||
|
|
@ -57,6 +57,11 @@
|
||||||
Edit your <a href="/admin/bio">bio</a>.
|
Edit your <a href="/admin/bio">bio</a>.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
<h2>About</h2>
|
||||||
|
<p>
|
||||||
|
Edit your <a href="/admin/about">about page</a>.
|
||||||
|
</p>
|
||||||
|
|
||||||
<h2>Passkeys</h2>
|
<h2>Passkeys</h2>
|
||||||
<p>
|
<p>
|
||||||
Manager <a href="/admin/passkeys">your passkeys</a>.
|
Manager <a href="/admin/passkeys">your passkeys</a>.
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,7 @@
|
||||||
<a href="/likes">Likes</a>
|
<a href="/likes">Likes</a>
|
||||||
<a href="/contacts">Contacts</a>
|
<a href="/contacts">Contacts</a>
|
||||||
<a href="/projects">Projects</a>
|
<a href="/projects">Projects</a>
|
||||||
|
<a href="/about">About</a>
|
||||||
<a href="{{ route('feed.notes.json') }}" class="rss-icon">@include('icons.json-feed', ['title' => 'JSON Feed'])</a>
|
<a href="{{ route('feed.notes.json') }}" class="rss-icon">@include('icons.json-feed', ['title' => 'JSON Feed'])</a>
|
||||||
</nav>
|
</nav>
|
||||||
<div id="theme-selector" role="region" aria-label="Theme switcher">
|
<div id="theme-selector" role="region" aria-label="Theme switcher">
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
use App\Http\Controllers\AboutPageController;
|
||||||
|
use App\Http\Controllers\Admin\AboutController;
|
||||||
use App\Http\Controllers\Admin\ArticlesController as AdminArticlesController;
|
use App\Http\Controllers\Admin\ArticlesController as AdminArticlesController;
|
||||||
use App\Http\Controllers\Admin\BioController;
|
use App\Http\Controllers\Admin\BioController;
|
||||||
use App\Http\Controllers\Admin\ClientsController;
|
use App\Http\Controllers\Admin\ClientsController;
|
||||||
|
|
@ -51,6 +53,9 @@ Route::view('projects', 'projects');
|
||||||
// Static colophon page
|
// Static colophon page
|
||||||
Route::view('colophon', 'colophon');
|
Route::view('colophon', 'colophon');
|
||||||
|
|
||||||
|
// About page
|
||||||
|
Route::get('about', [AboutPageController::class, 'show']);
|
||||||
|
|
||||||
// The login routes to get auth’d for admin
|
// The login routes to get auth’d for admin
|
||||||
Route::get('login', [AuthController::class, 'showLogin'])->name('login');
|
Route::get('login', [AuthController::class, 'showLogin'])->name('login');
|
||||||
Route::post('login', [AuthController::class, 'login']);
|
Route::post('login', [AuthController::class, 'login']);
|
||||||
|
|
@ -167,6 +172,12 @@ Route::middleware(MyAuthMiddleware::class)->prefix('admin')->group(function () {
|
||||||
Route::put('/', [SettingsController::class, 'update']);
|
Route::put('/', [SettingsController::class, 'update']);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// About
|
||||||
|
Route::prefix('about')->group(function () {
|
||||||
|
Route::get('/', [AboutController::class, 'show'])->name('admin.about.show');
|
||||||
|
Route::put('/', [AboutController::class, 'update']);
|
||||||
|
});
|
||||||
|
|
||||||
// Passkeys
|
// Passkeys
|
||||||
Route::prefix('passkeys')->group(function () {
|
Route::prefix('passkeys')->group(function () {
|
||||||
Route::get('/', [PasskeysController::class, 'index']);
|
Route::get('/', [PasskeysController::class, 'index']);
|
||||||
|
|
|
||||||
26
tests/Feature/AboutPageTest.php
Normal file
26
tests/Feature/AboutPageTest.php
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Tests\Feature;
|
||||||
|
|
||||||
|
use App\Models\About;
|
||||||
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
|
use PHPUnit\Framework\Attributes\Test;
|
||||||
|
use Tests\TestCase;
|
||||||
|
|
||||||
|
class AboutPageTest extends TestCase
|
||||||
|
{
|
||||||
|
use RefreshDatabase;
|
||||||
|
|
||||||
|
#[Test]
|
||||||
|
public function about_page_shows_content(): void
|
||||||
|
{
|
||||||
|
About::factory()->create([
|
||||||
|
'content' => 'This is the about page content.',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->get('/about')
|
||||||
|
->assertSee('This is the about page content.');
|
||||||
|
}
|
||||||
|
}
|
||||||
68
tests/Feature/Admin/AboutTest.php
Normal file
68
tests/Feature/Admin/AboutTest.php
Normal file
|
|
@ -0,0 +1,68 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Tests\Feature\Admin;
|
||||||
|
|
||||||
|
use App\Models\About;
|
||||||
|
use App\Models\User;
|
||||||
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
|
use PHPUnit\Framework\Attributes\Test;
|
||||||
|
use Tests\TestCase;
|
||||||
|
|
||||||
|
class AboutTest extends TestCase
|
||||||
|
{
|
||||||
|
use RefreshDatabase;
|
||||||
|
|
||||||
|
#[Test]
|
||||||
|
public function admin_about_page_loads(): void
|
||||||
|
{
|
||||||
|
$user = User::factory()->make();
|
||||||
|
|
||||||
|
$response = $this->actingAs($user)
|
||||||
|
->get('/admin/about');
|
||||||
|
$response->assertSeeText('Edit About');
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Test]
|
||||||
|
public function admin_can_create_about(): void
|
||||||
|
{
|
||||||
|
$user = User::factory()->make();
|
||||||
|
|
||||||
|
$this->actingAs($user)
|
||||||
|
->post('/admin/about', [
|
||||||
|
'_method' => 'PUT',
|
||||||
|
'content' => 'About content',
|
||||||
|
]);
|
||||||
|
$this->assertDatabaseHas('about', ['content' => 'About content']);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Test]
|
||||||
|
public function admin_can_load_existing_about(): void
|
||||||
|
{
|
||||||
|
$user = User::factory()->make();
|
||||||
|
$about = About::factory()->create([
|
||||||
|
'content' => 'This is <em>my</em> about page. It uses <strong>HTML</strong>.',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$response = $this->actingAs($user)
|
||||||
|
->get('/admin/about');
|
||||||
|
$response->assertSeeText('This is <em>my</em> about page. It uses <strong>HTML</strong>.');
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Test]
|
||||||
|
public function admin_can_edit_about(): void
|
||||||
|
{
|
||||||
|
$user = User::factory()->make();
|
||||||
|
$about = About::factory()->create();
|
||||||
|
|
||||||
|
$this->actingAs($user)
|
||||||
|
->post('/admin/about', [
|
||||||
|
'_method' => 'PUT',
|
||||||
|
'content' => 'This about page has been edited',
|
||||||
|
]);
|
||||||
|
$this->assertDatabaseHas('about', [
|
||||||
|
'content' => 'This about page has been edited',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue