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:
70
database/factories/ApplicationFactory.php
Normal file
70
database/factories/ApplicationFactory.php
Normal file
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Database\Factories;
|
||||
|
||||
use App\Enums\ApplicationKind;
|
||||
use App\Enums\ApplicationStatus;
|
||||
use App\Models\Application;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
|
||||
/**
|
||||
* @extends Factory<Application>
|
||||
*/
|
||||
class ApplicationFactory extends Factory
|
||||
{
|
||||
protected $model = Application::class;
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function definition(): array
|
||||
{
|
||||
return [
|
||||
'call_id' => null,
|
||||
'call_title' => null,
|
||||
'kind' => ApplicationKind::Contribute,
|
||||
'status' => ApplicationStatus::New,
|
||||
'name' => fake()->name(),
|
||||
'email' => fake()->unique()->safeEmail(),
|
||||
'organisation' => null,
|
||||
'message' => fake()->paragraph(),
|
||||
'hours_per_week' => fake()->randomElement([2, 3, 4, 6, null]),
|
||||
'links' => ['github' => 'https://github.com/'.fake()->userName()],
|
||||
'locale' => (string) config('locales.default'),
|
||||
'ip_hash' => Application::hashIp(fake()->ipv4()),
|
||||
];
|
||||
}
|
||||
|
||||
public function forCall(int $callId, string $title = 'Backend'): static
|
||||
{
|
||||
return $this->state(fn () => [
|
||||
'call_id' => $callId,
|
||||
'call_title' => $title,
|
||||
]);
|
||||
}
|
||||
|
||||
public function request(): static
|
||||
{
|
||||
return $this->state(fn () => [
|
||||
'kind' => ApplicationKind::Request,
|
||||
'organisation' => fake()->company(),
|
||||
'hours_per_week' => null,
|
||||
'links' => null,
|
||||
]);
|
||||
}
|
||||
|
||||
public function handled(ApplicationStatus $status = ApplicationStatus::Contacted): static
|
||||
{
|
||||
return $this->state(fn () => [
|
||||
'status' => $status,
|
||||
'handled_at' => now(),
|
||||
]);
|
||||
}
|
||||
|
||||
public function spam(): static
|
||||
{
|
||||
return $this->state(fn () => ['status' => ApplicationStatus::Spam]);
|
||||
}
|
||||
}
|
||||
61
database/factories/CallFactory.php
Normal file
61
database/factories/CallFactory.php
Normal file
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Database\Factories;
|
||||
|
||||
use App\Enums\CallStatus;
|
||||
use App\Models\Call;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
/**
|
||||
* @extends Factory<Call>
|
||||
*/
|
||||
class CallFactory extends Factory
|
||||
{
|
||||
protected $model = Call::class;
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function definition(): array
|
||||
{
|
||||
$role = fake()->randomElement([
|
||||
'Frontend', 'Backend', 'Diseño', 'DevOps', 'QA', 'Documentación',
|
||||
]);
|
||||
$title = "{$role} · ".rtrim(fake()->unique()->catchPhrase(), '.');
|
||||
|
||||
return [
|
||||
'title' => $this->eachLocale(fn (string $l) => "{$title} ({$l})"),
|
||||
'slug' => $this->eachLocale(fn (string $l) => Str::slug("{$title}-{$l}")),
|
||||
'description' => $this->eachLocale(fn () => fake()->paragraph()),
|
||||
'what_you_get' => $this->eachLocale(fn () => fake()->sentence(12)),
|
||||
'status' => CallStatus::Open,
|
||||
// The honest ask: a few hours a week, for a bounded stretch.
|
||||
'hours_per_week' => fake()->randomElement([2, 3, 4, 5, 6, 8]),
|
||||
'duration_weeks' => fake()->randomElement([4, 8, 12, 24]),
|
||||
'seniority' => fake()->randomElement(['junior', 'mid', 'any']),
|
||||
'opens_at' => now()->subWeeks(2),
|
||||
'closes_at' => fake()->optional(0.5)->dateTimeBetween('+2 weeks', '+3 months'),
|
||||
];
|
||||
}
|
||||
|
||||
public function closed(): static
|
||||
{
|
||||
return $this->state(fn () => ['status' => CallStatus::Closed]);
|
||||
}
|
||||
|
||||
public function filled(): static
|
||||
{
|
||||
return $this->state(fn () => ['status' => CallStatus::Filled]);
|
||||
}
|
||||
|
||||
/** @return array<string, string> */
|
||||
protected function eachLocale(callable $value): array
|
||||
{
|
||||
return collect(array_keys(config('locales.supported')))
|
||||
->mapWithKeys(fn (string $l) => [$l => $value($l)])
|
||||
->all();
|
||||
}
|
||||
}
|
||||
47
database/factories/CategoryFactory.php
Normal file
47
database/factories/CategoryFactory.php
Normal file
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Database\Factories;
|
||||
|
||||
use App\Models\Category;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
/**
|
||||
* @extends Factory<Category>
|
||||
*/
|
||||
class CategoryFactory extends Factory
|
||||
{
|
||||
protected $model = Category::class;
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function definition(): array
|
||||
{
|
||||
$name = $this->faker->unique()->words(2, true);
|
||||
|
||||
return [
|
||||
// Every supported locale gets a value so factory-made records
|
||||
// exercise the translation paths rather than the fallback.
|
||||
'name' => $this->eachLocale(fn (string $locale) => ucfirst($name)." ({$locale})"),
|
||||
'slug' => $this->eachLocale(fn (string $locale) => Str::slug($name."-{$locale}")),
|
||||
'description' => $this->eachLocale(fn () => $this->faker->sentence()),
|
||||
'color' => $this->faker->hexColor(),
|
||||
'position' => $this->faker->numberBetween(0, 20),
|
||||
'is_visible' => true,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param callable(string): string $value
|
||||
* @return array<string, string>
|
||||
*/
|
||||
protected function eachLocale(callable $value): array
|
||||
{
|
||||
return collect(array_keys(config('locales.supported')))
|
||||
->mapWithKeys(fn (string $locale) => [$locale => $value($locale)])
|
||||
->all();
|
||||
}
|
||||
}
|
||||
50
database/factories/ClientFactory.php
Normal file
50
database/factories/ClientFactory.php
Normal file
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Database\Factories;
|
||||
|
||||
use App\Models\Client;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
/**
|
||||
* @extends Factory<Client>
|
||||
*/
|
||||
class ClientFactory extends Factory
|
||||
{
|
||||
protected $model = Client::class;
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function definition(): array
|
||||
{
|
||||
$name = fake()->unique()->company();
|
||||
|
||||
return [
|
||||
'name' => $name,
|
||||
'slug' => Str::slug($name),
|
||||
'sector' => $this->eachLocale(fn () => fake()->randomElement([
|
||||
'ONG', 'Educación', 'Cultura', 'Salud', 'Medio ambiente',
|
||||
])),
|
||||
'logo' => null,
|
||||
'url' => fake()->optional(0.7)->url(),
|
||||
'is_public' => true,
|
||||
'position' => 0,
|
||||
];
|
||||
}
|
||||
|
||||
public function private(): static
|
||||
{
|
||||
return $this->state(fn () => ['is_public' => false]);
|
||||
}
|
||||
|
||||
/** @return array<string, string> */
|
||||
protected function eachLocale(callable $value): array
|
||||
{
|
||||
return collect(array_keys(config('locales.supported')))
|
||||
->mapWithKeys(fn (string $l) => [$l => $value($l)])
|
||||
->all();
|
||||
}
|
||||
}
|
||||
38
database/factories/ImageAttachmentFactory.php
Normal file
38
database/factories/ImageAttachmentFactory.php
Normal file
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Database\Factories;
|
||||
|
||||
use App\Models\ImageAttachment;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
|
||||
/**
|
||||
* @extends Factory<ImageAttachment>
|
||||
*/
|
||||
class ImageAttachmentFactory extends Factory
|
||||
{
|
||||
protected $model = ImageAttachment::class;
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function definition(): array
|
||||
{
|
||||
return [
|
||||
'collection' => 'gallery',
|
||||
'path' => 'projects/'.fake()->unique()->uuid().'.jpg',
|
||||
'alt' => $this->eachLocale(fn () => fake()->sentence(6)),
|
||||
'caption' => null,
|
||||
'position' => 0,
|
||||
];
|
||||
}
|
||||
|
||||
/** @return array<string, string> */
|
||||
protected function eachLocale(callable $value): array
|
||||
{
|
||||
return collect(array_keys(config('locales.supported')))
|
||||
->mapWithKeys(fn (string $l) => [$l => $value($l)])
|
||||
->all();
|
||||
}
|
||||
}
|
||||
57
database/factories/MediaFactory.php
Normal file
57
database/factories/MediaFactory.php
Normal file
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Database\Factories;
|
||||
|
||||
use App\Models\Media;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
|
||||
/**
|
||||
* @extends Factory<Media>
|
||||
*/
|
||||
class MediaFactory extends Factory
|
||||
{
|
||||
protected $model = Media::class;
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function definition(): array
|
||||
{
|
||||
return [
|
||||
'disk' => (string) config('media.disk', 'public'),
|
||||
'path' => 'projects/'.fake()->unique()->uuid().'.jpg',
|
||||
'mime' => 'image/jpeg',
|
||||
'size' => fake()->numberBetween(50_000, 900_000),
|
||||
'width' => null,
|
||||
'height' => null,
|
||||
'variants' => null,
|
||||
'placeholder' => null,
|
||||
'processed_at' => null,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* An image the processor has already been through.
|
||||
*/
|
||||
public function processed(int $width = 1600, int $height = 900): static
|
||||
{
|
||||
return $this->state(fn (array $attributes) => [
|
||||
'width' => $width,
|
||||
'height' => $height,
|
||||
'placeholder' => 'data:image/webp;base64,UklGRhoAAABXRUJQ',
|
||||
'variants' => collect([400, 800, 1200])
|
||||
->filter(fn (int $w) => $w < $width)
|
||||
->map(fn (int $w) => [
|
||||
'format' => 'webp',
|
||||
'width' => $w,
|
||||
'path' => str_replace('.jpg', "-{$w}.webp", $attributes['path']),
|
||||
'size' => $w * 100,
|
||||
])
|
||||
->values()
|
||||
->all(),
|
||||
'processed_at' => now(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
69
database/factories/MemberFactory.php
Normal file
69
database/factories/MemberFactory.php
Normal file
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Database\Factories;
|
||||
|
||||
use App\Models\Member;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
/**
|
||||
* @extends Factory<Member>
|
||||
*/
|
||||
class MemberFactory extends Factory
|
||||
{
|
||||
protected $model = Member::class;
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function definition(): array
|
||||
{
|
||||
$name = fake()->name();
|
||||
$handle = Str::slug($name).fake()->unique()->numberBetween(1, 9999);
|
||||
|
||||
return [
|
||||
'user_id' => null,
|
||||
'name' => $name,
|
||||
'slug' => $handle,
|
||||
'headline' => $this->eachLocale(fn (string $l) => fake()->jobTitle()." ({$l})"),
|
||||
'bio' => $this->eachLocale(fn () => fake()->paragraph()),
|
||||
'github_username' => $handle,
|
||||
'links' => [
|
||||
'github' => "https://github.com/{$handle}",
|
||||
'website' => fake()->optional()->url(),
|
||||
],
|
||||
'is_public' => true,
|
||||
'is_founder' => false,
|
||||
'joined_at' => fake()->dateTimeBetween('-4 years'),
|
||||
'position' => fake()->numberBetween(0, 20),
|
||||
];
|
||||
}
|
||||
|
||||
public function founder(): static
|
||||
{
|
||||
return $this->state(fn () => ['is_founder' => true]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Someone who has moved on. Their work stays on the site.
|
||||
*/
|
||||
public function past(): static
|
||||
{
|
||||
return $this->state(fn () => ['left_at' => fake()->dateTimeBetween('-1 year')]);
|
||||
}
|
||||
|
||||
public function hidden(): static
|
||||
{
|
||||
return $this->state(fn () => ['is_public' => false]);
|
||||
}
|
||||
|
||||
/** @return array<string, string> */
|
||||
protected function eachLocale(callable $value): array
|
||||
{
|
||||
return collect(array_keys(config('locales.supported')))
|
||||
->mapWithKeys(fn (string $l) => [$l => $value($l)])
|
||||
->all();
|
||||
}
|
||||
}
|
||||
56
database/factories/MissingUrlFactory.php
Normal file
56
database/factories/MissingUrlFactory.php
Normal file
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Database\Factories;
|
||||
|
||||
use App\Enums\MissingUrlStatus;
|
||||
use App\Models\MissingUrl;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
|
||||
/**
|
||||
* @extends Factory<MissingUrl>
|
||||
*/
|
||||
class MissingUrlFactory extends Factory
|
||||
{
|
||||
protected $model = MissingUrl::class;
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function definition(): array
|
||||
{
|
||||
return [
|
||||
'path' => '/'.fake()->unique()->slug(),
|
||||
'locale' => 'es',
|
||||
'hits' => fake()->numberBetween(1, 40),
|
||||
'referrer' => null,
|
||||
'status' => MissingUrlStatus::Pending,
|
||||
'first_seen_at' => now()->subDays(3),
|
||||
'last_seen_at' => now(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Linked from our own site: a page we publish points at a dead address.
|
||||
*/
|
||||
public function internal(): static
|
||||
{
|
||||
return $this->state(fn () => [
|
||||
'referrer' => rtrim((string) config('site.url'), '/').'/'.fake()->slug(),
|
||||
]);
|
||||
}
|
||||
|
||||
public function ignored(): static
|
||||
{
|
||||
return $this->state(fn () => ['status' => MissingUrlStatus::Ignored]);
|
||||
}
|
||||
|
||||
public function resolved(): static
|
||||
{
|
||||
return $this->state(fn () => [
|
||||
'status' => MissingUrlStatus::Resolved,
|
||||
'resolved_at' => now(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
91
database/factories/PageFactory.php
Normal file
91
database/factories/PageFactory.php
Normal file
@@ -0,0 +1,91 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Database\Factories;
|
||||
|
||||
use App\Enums\PageStatus;
|
||||
use App\Models\Category;
|
||||
use App\Models\Page;
|
||||
use App\Models\User;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
/**
|
||||
* @extends Factory<Page>
|
||||
*/
|
||||
class PageFactory extends Factory
|
||||
{
|
||||
protected $model = Page::class;
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function definition(): array
|
||||
{
|
||||
$title = $this->faker->unique()->sentence(4);
|
||||
$status = $this->faker->randomElement(PageStatus::cases());
|
||||
|
||||
return [
|
||||
'title' => $this->eachLocale(fn (string $locale) => rtrim($title, '.')." ({$locale})"),
|
||||
'slug' => $this->eachLocale(fn (string $locale) => Str::slug($title."-{$locale}")),
|
||||
'excerpt' => $this->eachLocale(fn () => $this->faker->sentence(12)),
|
||||
'body' => $this->eachLocale(fn () => '<p>'.implode('</p><p>', $this->faker->paragraphs(3)).'</p>'),
|
||||
'meta_title' => $this->eachLocale(fn () => Str::limit($title, 55, '')),
|
||||
'meta_description' => $this->eachLocale(fn () => $this->faker->sentence(14)),
|
||||
|
||||
'category_id' => Category::factory(),
|
||||
'author_id' => User::factory(),
|
||||
'status' => $status,
|
||||
'is_featured' => $this->faker->boolean(20),
|
||||
'published_at' => $status === PageStatus::Published
|
||||
? $this->faker->dateTimeBetween('-6 months')
|
||||
: null,
|
||||
];
|
||||
}
|
||||
|
||||
public function published(): static
|
||||
{
|
||||
return $this->state(fn () => [
|
||||
'status' => PageStatus::Published,
|
||||
'published_at' => now()->subDays(fake()->numberBetween(0, 30)),
|
||||
]);
|
||||
}
|
||||
|
||||
public function draft(): static
|
||||
{
|
||||
return $this->state(fn () => [
|
||||
'status' => PageStatus::Draft,
|
||||
'published_at' => null,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* A page translated only into the default locale — the state the
|
||||
* "missing translation" filters and the dashboard stat exist to surface.
|
||||
*/
|
||||
public function untranslated(): static
|
||||
{
|
||||
return $this->state(function (array $attributes): array {
|
||||
$default = (string) config('locales.default');
|
||||
|
||||
return [
|
||||
'title' => [$default => $attributes['title'][$default]],
|
||||
'slug' => [$default => $attributes['slug'][$default]],
|
||||
'excerpt' => [$default => $attributes['excerpt'][$default]],
|
||||
'body' => [$default => $attributes['body'][$default]],
|
||||
];
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param callable(string): string $value
|
||||
* @return array<string, string>
|
||||
*/
|
||||
protected function eachLocale(callable $value): array
|
||||
{
|
||||
return collect(array_keys(config('locales.supported')))
|
||||
->mapWithKeys(fn (string $locale) => [$locale => $value($locale)])
|
||||
->all();
|
||||
}
|
||||
}
|
||||
93
database/factories/ProjectFactory.php
Normal file
93
database/factories/ProjectFactory.php
Normal file
@@ -0,0 +1,93 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Database\Factories;
|
||||
|
||||
use App\Enums\ProjectStatus;
|
||||
use App\Models\Project;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
/**
|
||||
* @extends Factory<Project>
|
||||
*/
|
||||
class ProjectFactory extends Factory
|
||||
{
|
||||
protected $model = Project::class;
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function definition(): array
|
||||
{
|
||||
$title = rtrim(fake()->unique()->catchPhrase(), '.');
|
||||
$status = fake()->randomElement(ProjectStatus::cases());
|
||||
|
||||
return [
|
||||
'title' => $this->eachLocale(fn (string $l) => "{$title} ({$l})"),
|
||||
'slug' => $this->eachLocale(fn (string $l) => Str::slug("{$title}-{$l}")),
|
||||
'tagline' => $this->eachLocale(fn () => fake()->sentence(8)),
|
||||
'description' => $this->eachLocale(fn () => '<p>'.implode('</p><p>', fake()->paragraphs(3)).'</p>'),
|
||||
'decisions' => $this->eachLocale(fn () => '<p>'.fake()->paragraph().'</p>'),
|
||||
'postmortem' => null,
|
||||
'meta_title' => $this->eachLocale(fn () => Str::limit($title, 55, '')),
|
||||
'meta_description' => $this->eachLocale(fn () => fake()->sentence(14)),
|
||||
|
||||
'status' => $status,
|
||||
|
||||
'demo_url' => fake()->optional()->url(),
|
||||
'is_featured' => fake()->boolean(20),
|
||||
'needs_help' => fake()->boolean(30),
|
||||
'started_at' => fake()->dateTimeBetween('-3 years'),
|
||||
'published_at' => now()->subDays(fake()->numberBetween(1, 400)),
|
||||
'position' => fake()->numberBetween(0, 20),
|
||||
];
|
||||
}
|
||||
|
||||
public function published(): static
|
||||
{
|
||||
return $this->state(fn () => ['published_at' => now()->subDay()]);
|
||||
}
|
||||
|
||||
public function draft(): static
|
||||
{
|
||||
return $this->state(fn () => ['published_at' => null]);
|
||||
}
|
||||
|
||||
public function active(): static
|
||||
{
|
||||
return $this->state(fn () => ['status' => ProjectStatus::Active]);
|
||||
}
|
||||
|
||||
public function needingHelp(): static
|
||||
{
|
||||
return $this->state(fn () => [
|
||||
'needs_help' => true,
|
||||
'status' => ProjectStatus::Active,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* A finished project with the write-up that makes it worth reading — the
|
||||
* kind of content a commercial consultancy cannot publish.
|
||||
*/
|
||||
public function withPostmortem(): static
|
||||
{
|
||||
return $this->state(fn () => [
|
||||
'status' => ProjectStatus::Archived,
|
||||
'ended_at' => fake()->dateTimeBetween('-1 year'),
|
||||
'postmortem' => $this->eachLocale(
|
||||
fn () => '<p>'.implode('</p><p>', fake()->paragraphs(2)).'</p>',
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
||||
/** @return array<string, string> */
|
||||
protected function eachLocale(callable $value): array
|
||||
{
|
||||
return collect(array_keys(config('locales.supported')))
|
||||
->mapWithKeys(fn (string $l) => [$l => $value($l)])
|
||||
->all();
|
||||
}
|
||||
}
|
||||
36
database/factories/RedirectFactory.php
Normal file
36
database/factories/RedirectFactory.php
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Database\Factories;
|
||||
|
||||
use App\Models\Redirect;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
|
||||
/**
|
||||
* @extends Factory<Redirect>
|
||||
*/
|
||||
class RedirectFactory extends Factory
|
||||
{
|
||||
protected $model = Redirect::class;
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function definition(): array
|
||||
{
|
||||
return [
|
||||
'from' => '/'.fake()->unique()->slug(),
|
||||
'to' => '/'.fake()->slug(),
|
||||
'status' => 301,
|
||||
'locale' => null,
|
||||
'is_enabled' => true,
|
||||
'hits' => 0,
|
||||
];
|
||||
}
|
||||
|
||||
public function disabled(): static
|
||||
{
|
||||
return $this->state(fn () => ['is_enabled' => false]);
|
||||
}
|
||||
}
|
||||
76
database/factories/RepositoryFactory.php
Normal file
76
database/factories/RepositoryFactory.php
Normal file
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Database\Factories;
|
||||
|
||||
use App\Enums\GitProvider;
|
||||
use App\Models\Repository;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
/**
|
||||
* @extends Factory<Repository>
|
||||
*/
|
||||
class RepositoryFactory extends Factory
|
||||
{
|
||||
protected $model = Repository::class;
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function definition(): array
|
||||
{
|
||||
$owner = 'colectivo';
|
||||
$name = Str::slug(fake()->unique()->words(2, true));
|
||||
|
||||
return [
|
||||
'provider' => GitProvider::Gitea,
|
||||
'full_name' => "{$owner}/{$name}",
|
||||
'is_primary' => true,
|
||||
'show_publicly' => true,
|
||||
|
||||
'html_url' => "https://git.pireon.test/{$owner}/{$name}",
|
||||
'description' => fake()->sentence(),
|
||||
'default_branch' => 'main',
|
||||
'primary_language' => fake()->randomElement(['PHP', 'TypeScript', 'Rust', 'Go', 'Python']),
|
||||
'license' => fake()->randomElement(['MIT', 'AGPL-3.0', 'Apache-2.0']),
|
||||
'topics' => ['open-source', 'community'],
|
||||
'languages' => ['PHP' => 120000, 'Blade' => 30000],
|
||||
'stars' => fake()->numberBetween(0, 400),
|
||||
'forks' => fake()->numberBetween(0, 40),
|
||||
'watchers' => fake()->numberBetween(0, 30),
|
||||
'open_issues_count' => fake()->numberBetween(0, 25),
|
||||
'contributors_count' => fake()->numberBetween(1, 12),
|
||||
'is_archived' => false,
|
||||
'is_fork' => false,
|
||||
'pushed_at' => fake()->dateTimeBetween('-2 months'),
|
||||
'repo_created_at' => fake()->dateTimeBetween('-4 years', '-1 year'),
|
||||
'last_synced_at' => now(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Nothing pushed for a long time — what the public site uses to avoid
|
||||
* advertising dead work as live.
|
||||
*/
|
||||
public function dormant(): static
|
||||
{
|
||||
return $this->state(fn () => [
|
||||
'pushed_at' => now()->subYears(2),
|
||||
]);
|
||||
}
|
||||
|
||||
public function failing(): static
|
||||
{
|
||||
return $this->state(fn () => [
|
||||
'sync_error' => 'The repository [colectivo/gone] does not exist.',
|
||||
'sync_failures' => 5,
|
||||
]);
|
||||
}
|
||||
|
||||
public function hidden(): static
|
||||
{
|
||||
return $this->state(fn () => ['show_publicly' => false]);
|
||||
}
|
||||
}
|
||||
57
database/factories/ServiceFactory.php
Normal file
57
database/factories/ServiceFactory.php
Normal file
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Database\Factories;
|
||||
|
||||
use App\Models\Service;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
/**
|
||||
* @extends Factory<Service>
|
||||
*/
|
||||
class ServiceFactory extends Factory
|
||||
{
|
||||
protected $model = Service::class;
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function definition(): array
|
||||
{
|
||||
$title = fake()->unique()->randomElement([
|
||||
'Desarrollo web', 'Acompañamiento técnico', 'Formación',
|
||||
'Auditoría de accesibilidad', 'Infraestructura', 'Diseño de producto',
|
||||
]).' '.fake()->unique()->numberBetween(1, 999);
|
||||
|
||||
return [
|
||||
'title' => $this->eachLocale(fn (string $l) => "{$title} ({$l})"),
|
||||
'slug' => $this->eachLocale(fn (string $l) => Str::slug("{$title}-{$l}")),
|
||||
'tagline' => $this->eachLocale(fn () => fake()->sentence(8)),
|
||||
'description' => $this->eachLocale(fn () => fake()->paragraph()),
|
||||
'icon' => 'heroicon-o-code-bracket',
|
||||
'is_published' => true,
|
||||
'is_featured' => false,
|
||||
'position' => 0,
|
||||
];
|
||||
}
|
||||
|
||||
public function draft(): static
|
||||
{
|
||||
return $this->state(fn () => ['is_published' => false]);
|
||||
}
|
||||
|
||||
public function featured(): static
|
||||
{
|
||||
return $this->state(fn () => ['is_featured' => true]);
|
||||
}
|
||||
|
||||
/** @return array<string, string> */
|
||||
protected function eachLocale(callable $value): array
|
||||
{
|
||||
return collect(array_keys(config('locales.supported')))
|
||||
->mapWithKeys(fn (string $l) => [$l => $value($l)])
|
||||
->all();
|
||||
}
|
||||
}
|
||||
44
database/factories/TalkFactory.php
Normal file
44
database/factories/TalkFactory.php
Normal file
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Database\Factories;
|
||||
|
||||
use App\Models\Talk;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
|
||||
/**
|
||||
* @extends Factory<Talk>
|
||||
*/
|
||||
class TalkFactory extends Factory
|
||||
{
|
||||
protected $model = Talk::class;
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function definition(): array
|
||||
{
|
||||
return [
|
||||
'title' => rtrim(fake()->unique()->catchPhrase(), '.'),
|
||||
'language' => (string) config('locales.default'),
|
||||
'summary' => $this->eachLocale(fn () => fake()->sentence(14)),
|
||||
'event' => fake()->randomElement([
|
||||
'Codemotion', 'T3chFest', 'JSDay', 'PHPBarcelona', 'Meetup Laravel',
|
||||
]),
|
||||
'city' => fake()->randomElement(['Madrid', 'Barcelona', 'Valencia', 'Sevilla']),
|
||||
'held_at' => fake()->dateTimeBetween('-3 years'),
|
||||
'video_url' => fake()->optional(0.6)->url(),
|
||||
'slides_url' => fake()->optional(0.4)->url(),
|
||||
'is_public' => true,
|
||||
];
|
||||
}
|
||||
|
||||
/** @return array<string, string> */
|
||||
protected function eachLocale(callable $value): array
|
||||
{
|
||||
return collect(array_keys(config('locales.supported')))
|
||||
->mapWithKeys(fn (string $l) => [$l => $value($l)])
|
||||
->all();
|
||||
}
|
||||
}
|
||||
60
database/factories/TechnologyFactory.php
Normal file
60
database/factories/TechnologyFactory.php
Normal file
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Database\Factories;
|
||||
|
||||
use App\Enums\RadarRing;
|
||||
use App\Enums\TechnologyCategory;
|
||||
use App\Models\Technology;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
/**
|
||||
* @extends Factory<Technology>
|
||||
*/
|
||||
class TechnologyFactory extends Factory
|
||||
{
|
||||
protected $model = Technology::class;
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function definition(): array
|
||||
{
|
||||
$name = fake()->unique()->randomElement([
|
||||
'Laravel', 'Astro', 'PHP', 'TypeScript', 'Rust', 'Go', 'PostgreSQL',
|
||||
'Redis', 'Docker', 'Tailwind CSS', 'Vue', 'Svelte', 'Python',
|
||||
'Kubernetes', 'Terraform', 'Elasticsearch', 'RabbitMQ', 'Vite',
|
||||
]).' '.fake()->unique()->numberBetween(1, 9999);
|
||||
|
||||
return [
|
||||
'name' => $name,
|
||||
'slug' => Str::slug($name),
|
||||
'description' => $this->eachLocale(fn () => fake()->sentence()),
|
||||
'radar_note' => $this->eachLocale(fn () => fake()->sentence(10)),
|
||||
'category' => fake()->randomElement(TechnologyCategory::cases()),
|
||||
'radar_ring' => fake()->randomElement(RadarRing::cases()),
|
||||
'color' => fake()->hexColor(),
|
||||
'position' => fake()->numberBetween(0, 30),
|
||||
];
|
||||
}
|
||||
|
||||
public function offRadar(): static
|
||||
{
|
||||
return $this->state(fn () => ['radar_ring' => null]);
|
||||
}
|
||||
|
||||
public function ring(RadarRing $ring): static
|
||||
{
|
||||
return $this->state(fn () => ['radar_ring' => $ring]);
|
||||
}
|
||||
|
||||
/** @return array<string, string> */
|
||||
protected function eachLocale(callable $value): array
|
||||
{
|
||||
return collect(array_keys(config('locales.supported')))
|
||||
->mapWithKeys(fn (string $l) => [$l => $value($l)])
|
||||
->all();
|
||||
}
|
||||
}
|
||||
46
database/factories/TestimonialFactory.php
Normal file
46
database/factories/TestimonialFactory.php
Normal file
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Database\Factories;
|
||||
|
||||
use App\Models\Testimonial;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
|
||||
/**
|
||||
* @extends Factory<Testimonial>
|
||||
*/
|
||||
class TestimonialFactory extends Factory
|
||||
{
|
||||
protected $model = Testimonial::class;
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function definition(): array
|
||||
{
|
||||
return [
|
||||
'quote' => $this->eachLocale(fn () => fake()->sentence(20)),
|
||||
'original_locale' => (string) config('locales.default'),
|
||||
'author_name' => fake()->name(),
|
||||
'author_role' => $this->eachLocale(fn () => fake()->jobTitle()),
|
||||
'avatar' => null,
|
||||
'is_public' => true,
|
||||
'position' => 0,
|
||||
'given_at' => fake()->optional(0.7)->dateTimeBetween('-2 years'),
|
||||
];
|
||||
}
|
||||
|
||||
public function private(): static
|
||||
{
|
||||
return $this->state(fn () => ['is_public' => false]);
|
||||
}
|
||||
|
||||
/** @return array<string, string> */
|
||||
protected function eachLocale(callable $value): array
|
||||
{
|
||||
return collect(array_keys(config('locales.supported')))
|
||||
->mapWithKeys(fn (string $l) => [$l => $value($l)])
|
||||
->all();
|
||||
}
|
||||
}
|
||||
59
database/factories/UserFactory.php
Normal file
59
database/factories/UserFactory.php
Normal file
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Database\Factories;
|
||||
|
||||
use App\Models\User;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
/**
|
||||
* @extends Factory<User>
|
||||
*/
|
||||
class UserFactory extends Factory
|
||||
{
|
||||
/**
|
||||
* Default state mirrors what the OAuth callback produces: an identity
|
||||
* copied from the server, with no local password.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function definition(): array
|
||||
{
|
||||
return [
|
||||
'oauth_id' => fake()->unique()->numberBetween(1000, 999999),
|
||||
'name' => fake()->name(),
|
||||
'email' => fake()->unique()->safeEmail(),
|
||||
'email_verified_at' => now(),
|
||||
'password' => null,
|
||||
'locale' => fake()->randomElement(array_keys(config('locales.supported'))),
|
||||
'is_active' => true,
|
||||
'remote_roles' => ['user'],
|
||||
'last_login_at' => now(),
|
||||
'remember_token' => Str::random(10),
|
||||
];
|
||||
}
|
||||
|
||||
public function inactive(): static
|
||||
{
|
||||
return $this->state(fn () => ['is_active' => false]);
|
||||
}
|
||||
|
||||
/**
|
||||
* A user who has never signed in through the identity server, used to
|
||||
* check that the callback adopts pre-existing local rows by email.
|
||||
*/
|
||||
public function unlinked(): static
|
||||
{
|
||||
return $this->state(fn () => [
|
||||
'oauth_id' => null,
|
||||
'last_login_at' => null,
|
||||
]);
|
||||
}
|
||||
|
||||
public function withRole(string $role): static
|
||||
{
|
||||
return $this->afterCreating(fn (User $user) => $user->assignRole($role));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user