Initial commit: Pireon Admin — headless admin panel and public API

Laravel 13 + Filament 5 admin panel and REST API for a non-profit developer
collective, backed by the Pireon OAuth identity server. The public site is a
separate, server-rendered application consuming /api/public/v1.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-19 17:55:27 +02:00
commit 3897113313
534 changed files with 64050 additions and 0 deletions

View File

@@ -0,0 +1,78 @@
<?php
declare(strict_types=1);
namespace App\Http\Resources\PublicApi;
use App\Models\Member;
use App\Support\ImagePayload;
use App\Support\SiteUrls;
use App\Support\TranslatablePayload;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
/**
* @mixin Member
*
* A member's profile is the real currency a volunteer collective pays people
* in, so it carries everything needed to render a proper personal page rather
* than a bare avatar and name.
*/
class PublicMemberResource extends JsonResource
{
/**
* @return array<string, mixed>
*/
public function toArray(Request $request): array
{
$locale = app()->getLocale();
return [
'id' => $this->id,
'name' => $this->name,
'slug' => $this->slug,
...TranslatablePayload::resolve($this->resource, ['headline', 'bio'], $locale),
// A portrait's description is the person's name, so there is no
// field to fill in and no way to get it wrong.
'avatar' => ImagePayload::make(
$this->avatar,
$this->resource->relationLoaded('avatarMedia') ? $this->resource->getRelation('avatarMedia') : null,
$this->avatarAlt(),
),
'github_username' => $this->github_username,
'links' => (object) ($this->links ?? []),
'is_founder' => $this->is_founder,
// Former members stay on the site with their history intact; the
// frontend decides how to present them.
'is_current' => $this->left_at === null,
'joined_at' => $this->joined_at?->toDateString(),
'left_at' => $this->left_at?->toDateString(),
'role' => $this->whenPivotLoaded('member_project', fn () => $this->pivot->role),
'technologies' => PublicTechnologyResource::collection($this->whenLoaded('technologies')),
'projects' => PublicProjectResource::collection($this->whenLoaded('projects')),
'alternates' => $this->memberAlternates(),
'path' => SiteUrls::path('member', $locale, $this->slug),
];
}
/**
* A member's slug is a single string rather than a translated map, so the
* same slug is valid in every locale — only the path prefix changes.
*
* @return array<string, string>
*/
protected function memberAlternates(): array
{
return collect(array_keys(config('locales.supported')))
->mapWithKeys(fn (string $locale) => [
$locale => SiteUrls::path('member', $locale, $this->slug),
])
->all();
}
}