Commit workspace changes

This commit is contained in:
2026-04-05 19:42:33 +02:00
parent 148a3bbe43
commit 08ad757bcb
312 changed files with 35149 additions and 399 deletions

View File

@@ -28,6 +28,7 @@ final class ArtworkCreateRequest extends FormRequest
'tags' => 'nullable|string|max:200',
'license' => 'nullable|boolean',
'is_mature' => 'nullable|boolean',
'group' => 'nullable|string|max:90',
];
}

View File

@@ -39,6 +39,7 @@ class StoreCollectionRequest extends FormRequest
return [
'title' => ['required', 'string', 'min:2', 'max:120'],
'slug' => ['required', 'string', 'min:2', 'max:140', 'regex:/^[a-z0-9]+(?:-[a-z0-9]+)*$/'],
'group' => ['nullable', 'string', 'max:90'],
'type' => ['nullable', 'in:' . implode(',', [
Collection::TYPE_PERSONAL,
Collection::TYPE_COMMUNITY,

View File

@@ -45,6 +45,7 @@ class UpdateCollectionRequest extends FormRequest
return [
'title' => ['required', 'string', 'min:2', 'max:120'],
'slug' => ['required', 'string', 'min:2', 'max:140', 'regex:/^[a-z0-9]+(?:-[a-z0-9]+)*$/'],
'group' => ['nullable', 'string', 'max:90'],
'type' => ['nullable', 'in:' . implode(',', [
Collection::TYPE_PERSONAL,
Collection::TYPE_COMMUNITY,

View File

@@ -0,0 +1,22 @@
<?php
declare(strict_types=1);
namespace App\Http\Requests\Groups;
use Illuminate\Foundation\Http\FormRequest;
class AttachArtworkToGroupChallengeRequest extends FormRequest
{
public function authorize(): bool
{
return $this->user() !== null;
}
public function rules(): array
{
return [
'artwork_id' => ['required', 'integer', 'exists:artworks,id'],
];
}
}

View File

@@ -0,0 +1,22 @@
<?php
declare(strict_types=1);
namespace App\Http\Requests\Groups;
use Illuminate\Foundation\Http\FormRequest;
class AttachArtworkToGroupProjectRequest extends FormRequest
{
public function authorize(): bool
{
return $this->user() !== null;
}
public function rules(): array
{
return [
'artwork_id' => ['required', 'integer', 'exists:artworks,id'],
];
}
}

View File

@@ -0,0 +1,22 @@
<?php
declare(strict_types=1);
namespace App\Http\Requests\Groups;
use Illuminate\Foundation\Http\FormRequest;
class AttachArtworkToGroupReleaseRequest extends FormRequest
{
public function authorize(): bool
{
return $this->user() !== null;
}
public function rules(): array
{
return [
'artwork_id' => ['required', 'integer'],
];
}
}

View File

@@ -0,0 +1,22 @@
<?php
declare(strict_types=1);
namespace App\Http\Requests\Groups;
use Illuminate\Foundation\Http\FormRequest;
class AttachAssetToGroupProjectRequest extends FormRequest
{
public function authorize(): bool
{
return $this->user() !== null;
}
public function rules(): array
{
return [
'asset_id' => ['required', 'integer', 'exists:group_assets,id'],
];
}
}

View File

@@ -0,0 +1,23 @@
<?php
declare(strict_types=1);
namespace App\Http\Requests\Groups;
use Illuminate\Foundation\Http\FormRequest;
class AttachContributorToGroupReleaseRequest extends FormRequest
{
public function authorize(): bool
{
return $this->user() !== null;
}
public function rules(): array
{
return [
'user_id' => ['required', 'integer'],
'role_label' => ['nullable', 'string', 'max:80'],
];
}
}

View File

@@ -0,0 +1,22 @@
<?php
declare(strict_types=1);
namespace App\Http\Requests\Groups;
use Illuminate\Foundation\Http\FormRequest;
class PinGroupActivityItemRequest extends FormRequest
{
public function authorize(): bool
{
return $this->user() !== null;
}
public function rules(): array
{
return [
'is_pinned' => ['nullable', 'boolean'],
];
}
}

View File

@@ -0,0 +1,22 @@
<?php
declare(strict_types=1);
namespace App\Http\Requests\Groups;
use Illuminate\Foundation\Http\FormRequest;
class ReviewGroupArtworkRequest extends FormRequest
{
public function authorize(): bool
{
return $this->user() !== null;
}
public function rules(): array
{
return [
'review_notes' => ['nullable', 'string', 'max:2000'],
];
}
}

View File

@@ -0,0 +1,24 @@
<?php
declare(strict_types=1);
namespace App\Http\Requests\Groups;
use App\Models\Group;
use Illuminate\Foundation\Http\FormRequest;
class ReviewGroupJoinRequestRequest extends FormRequest
{
public function authorize(): bool
{
return $this->user() !== null;
}
public function rules(): array
{
return [
'role' => ['nullable', 'in:' . implode(',', [Group::ROLE_ADMIN, Group::ROLE_EDITOR, Group::ROLE_MEMBER, Group::ROLE_CONTRIBUTOR])],
'review_notes' => ['nullable', 'string', 'max:2000'],
];
}
}

View File

@@ -0,0 +1,29 @@
<?php
declare(strict_types=1);
namespace App\Http\Requests\Groups;
use Illuminate\Foundation\Http\FormRequest;
class StoreGroupAssetRequest extends FormRequest
{
public function authorize(): bool
{
return $this->user() !== null;
}
public function rules(): array
{
return [
'title' => ['required', 'string', 'min:2', 'max:180'],
'description' => ['nullable', 'string', 'max:40000'],
'category' => ['nullable', 'in:' . implode(',', (array) config('groups.assets.categories', []))],
'visibility' => ['nullable', 'in:' . implode(',', (array) config('groups.assets.visibility_options', []))],
'status' => ['nullable', 'in:' . implode(',', (array) config('groups.assets.statuses', []))],
'linked_project_id' => ['nullable', 'integer'],
'is_featured' => ['nullable', 'boolean'],
'file' => ['required', 'file', 'mimes:' . implode(',', (array) config('groups.assets.allowed_extensions', [])), 'max:' . (int) config('groups.assets.max_upload_kb', 20480)],
];
}
}

View File

@@ -0,0 +1,37 @@
<?php
declare(strict_types=1);
namespace App\Http\Requests\Groups;
use Illuminate\Foundation\Http\FormRequest;
class StoreGroupChallengeRequest extends FormRequest
{
public function authorize(): bool
{
return $this->user() !== null;
}
public function rules(): array
{
return [
'title' => ['required', 'string', 'min:2', 'max:180'],
'summary' => ['nullable', 'string', 'max:320'],
'description' => ['nullable', 'string', 'max:40000'],
'cover_path' => ['nullable', 'string', 'max:2048'],
'cover_file' => ['nullable', 'image', 'mimes:jpg,jpeg,png,webp', 'max:5120'],
'visibility' => ['nullable', 'in:' . implode(',', (array) config('groups.challenges.visibility_options', []))],
'participation_scope' => ['nullable', 'in:' . implode(',', (array) config('groups.challenges.participation_scopes', []))],
'status' => ['nullable', 'in:' . implode(',', (array) config('groups.challenges.statuses', []))],
'start_at' => ['nullable', 'date'],
'end_at' => ['nullable', 'date', 'after_or_equal:start_at'],
'rules_text' => ['nullable', 'string', 'max:20000'],
'submission_instructions' => ['nullable', 'string', 'max:20000'],
'judging_mode' => ['nullable', 'in:' . implode(',', (array) config('groups.challenges.judging_modes', []))],
'linked_collection_id' => ['nullable', 'integer'],
'linked_project_id' => ['nullable', 'integer'],
'featured_artwork_id' => ['nullable', 'integer'],
];
}
}

View File

@@ -0,0 +1,38 @@
<?php
declare(strict_types=1);
namespace App\Http\Requests\Groups;
use Illuminate\Foundation\Http\FormRequest;
class StoreGroupEventRequest extends FormRequest
{
public function authorize(): bool
{
return $this->user() !== null;
}
public function rules(): array
{
return [
'title' => ['required', 'string', 'min:2', 'max:180'],
'summary' => ['nullable', 'string', 'max:320'],
'description' => ['nullable', 'string', 'max:40000'],
'event_type' => ['nullable', 'in:' . implode(',', (array) config('groups.events.types', []))],
'visibility' => ['nullable', 'in:' . implode(',', (array) config('groups.events.visibility_options', []))],
'status' => ['nullable', 'in:' . implode(',', (array) config('groups.events.statuses', []))],
'start_at' => ['nullable', 'date'],
'end_at' => ['nullable', 'date', 'after_or_equal:start_at'],
'timezone' => ['nullable', 'string', 'max:80'],
'cover_path' => ['nullable', 'string', 'max:2048'],
'cover_file' => ['nullable', 'image', 'mimes:jpg,jpeg,png,webp', 'max:5120'],
'location' => ['nullable', 'string', 'max:180'],
'external_url' => ['nullable', 'url', 'max:2048'],
'linked_project_id' => ['nullable', 'integer'],
'linked_collection_id' => ['nullable', 'integer'],
'linked_challenge_id' => ['nullable', 'integer'],
'is_featured' => ['nullable', 'boolean'],
];
}
}

View File

@@ -0,0 +1,26 @@
<?php
declare(strict_types=1);
namespace App\Http\Requests\Groups;
use Illuminate\Foundation\Http\FormRequest;
class StoreGroupJoinRequestRequest extends FormRequest
{
public function authorize(): bool
{
return $this->user() !== null;
}
public function rules(): array
{
return [
'message' => ['nullable', 'string', 'max:2000'],
'portfolio_url' => ['nullable', 'url', 'max:2048'],
'desired_role' => ['nullable', 'string', 'max:32'],
'skills_json' => ['nullable', 'array', 'max:12'],
'skills_json.*' => ['string', 'max:80'],
];
}
}

View File

@@ -0,0 +1,26 @@
<?php
declare(strict_types=1);
namespace App\Http\Requests\Groups;
use App\Models\Group;
use Illuminate\Foundation\Http\FormRequest;
class StoreGroupMemberRequest extends FormRequest
{
public function authorize(): bool
{
return $this->user() !== null;
}
public function rules(): array
{
return [
'username' => ['required', 'string', 'max:20'],
'role' => ['required', 'in:' . implode(',', [Group::ROLE_ADMIN, Group::ROLE_EDITOR, Group::ROLE_MEMBER, Group::ROLE_CONTRIBUTOR])],
'note' => ['nullable', 'string', 'max:500'],
'expires_in_days' => ['nullable', 'integer', 'min:1', 'max:30'],
];
}
}

View File

@@ -0,0 +1,27 @@
<?php
declare(strict_types=1);
namespace App\Http\Requests\Groups;
use Illuminate\Foundation\Http\FormRequest;
class StoreGroupMilestoneRequest extends FormRequest
{
public function authorize(): bool
{
return $this->user() !== null;
}
public function rules(): array
{
return [
'title' => ['required', 'string', 'min:2', 'max:180'],
'summary' => ['nullable', 'string', 'max:320'],
'status' => ['nullable', 'in:' . implode(',', (array) config('groups.milestones.statuses', []))],
'due_date' => ['nullable', 'date'],
'owner_user_id' => ['nullable', 'integer'],
'notes' => ['nullable', 'string', 'max:40000'],
];
}
}

View File

@@ -0,0 +1,32 @@
<?php
declare(strict_types=1);
namespace App\Http\Requests\Groups;
use App\Models\GroupPost;
use Illuminate\Foundation\Http\FormRequest;
class StoreGroupPostRequest extends FormRequest
{
public function authorize(): bool
{
return $this->user() !== null;
}
public function rules(): array
{
return [
'type' => ['required', 'in:' . implode(',', [
GroupPost::TYPE_ANNOUNCEMENT,
GroupPost::TYPE_RELEASE,
GroupPost::TYPE_RECRUITMENT,
GroupPost::TYPE_UPDATE,
])],
'title' => ['required', 'string', 'min:2', 'max:180'],
'excerpt' => ['nullable', 'string', 'max:320'],
'content' => ['nullable', 'string', 'max:40000'],
'cover_path' => ['nullable', 'string', 'max:2048'],
];
}
}

View File

@@ -0,0 +1,36 @@
<?php
declare(strict_types=1);
namespace App\Http\Requests\Groups;
use Illuminate\Foundation\Http\FormRequest;
class StoreGroupProjectRequest extends FormRequest
{
public function authorize(): bool
{
return $this->user() !== null;
}
public function rules(): array
{
return [
'title' => ['required', 'string', 'min:2', 'max:180'],
'summary' => ['nullable', 'string', 'max:320'],
'description' => ['nullable', 'string', 'max:40000'],
'cover_path' => ['nullable', 'string', 'max:2048'],
'cover_file' => ['nullable', 'image', 'mimes:jpg,jpeg,png,webp', 'max:5120'],
'status' => ['nullable', 'in:' . implode(',', (array) config('groups.projects.statuses', []))],
'visibility' => ['nullable', 'in:' . implode(',', (array) config('groups.projects.visibility_options', []))],
'start_date' => ['nullable', 'date'],
'target_date' => ['nullable', 'date', 'after_or_equal:start_date'],
'lead_user_id' => ['nullable', 'integer'],
'linked_collection_id' => ['nullable', 'integer'],
'linked_featured_artwork_id' => ['nullable', 'integer'],
'pinned_post_id' => ['nullable', 'integer'],
'member_user_ids' => ['nullable', 'array'],
'member_user_ids.*' => ['integer'],
];
}
}

View File

@@ -0,0 +1,36 @@
<?php
declare(strict_types=1);
namespace App\Http\Requests\Groups;
use Illuminate\Foundation\Http\FormRequest;
class StoreGroupReleaseRequest extends FormRequest
{
public function authorize(): bool
{
return $this->user() !== null;
}
public function rules(): array
{
return [
'title' => ['required', 'string', 'min:2', 'max:180'],
'summary' => ['nullable', 'string', 'max:320'],
'description' => ['nullable', 'string', 'max:40000'],
'cover_path' => ['nullable', 'string', 'max:2048'],
'cover_file' => ['nullable', 'image', 'mimes:jpg,jpeg,png,webp', 'max:5120'],
'status' => ['nullable', 'in:' . implode(',', (array) config('groups.releases.statuses', []))],
'current_stage' => ['nullable', 'in:' . implode(',', (array) config('groups.releases.stages', []))],
'visibility' => ['nullable', 'in:' . implode(',', (array) config('groups.releases.visibility_options', []))],
'planned_release_at' => ['nullable', 'date'],
'lead_user_id' => ['nullable', 'integer'],
'linked_project_id' => ['nullable', 'integer'],
'linked_collection_id' => ['nullable', 'integer'],
'featured_artwork_id' => ['nullable', 'integer'],
'release_notes' => ['nullable', 'string', 'max:40000'],
'is_featured' => ['nullable', 'boolean'],
];
}
}

View File

@@ -0,0 +1,53 @@
<?php
declare(strict_types=1);
namespace App\Http\Requests\Groups;
use App\Models\Group;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Str;
class StoreGroupRequest extends FormRequest
{
public function authorize(): bool
{
return $this->user() !== null;
}
protected function prepareForValidation(): void
{
$name = (string) $this->input('name', '');
$slug = (string) $this->input('slug', '');
if ($slug === '' && $name !== '') {
$slug = Str::slug(Str::limit($name, 90, ''));
}
$this->merge([
'slug' => $slug,
]);
}
public function rules(): array
{
return [
'name' => ['required', 'string', 'min:2', 'max:80'],
'slug' => ['required', 'string', 'min:2', 'max:90', 'regex:/^[a-z0-9]+(?:-[a-z0-9]+)*$/'],
'headline' => ['nullable', 'string', 'max:160'],
'bio' => ['nullable', 'string', 'max:3000'],
'visibility' => ['required', 'in:' . implode(',', Group::acceptedVisibilityValues())],
'membership_policy' => ['nullable', 'in:' . implode(',', Group::acceptedMembershipPolicies())],
'type' => ['nullable', 'string', 'max:80'],
'founded_at' => ['nullable', 'date'],
'website_url' => ['nullable', 'url', 'max:2048'],
'links_json' => ['nullable', 'array', 'max:8'],
'links_json.*.label' => ['required_with:links_json', 'string', 'max:40'],
'links_json.*.url' => ['required_with:links_json', 'url', 'max:2048'],
'avatar_path' => ['nullable', 'string', 'max:2048'],
'banner_path' => ['nullable', 'string', 'max:2048'],
'avatar_file' => ['nullable', 'file', 'image', 'max:5120', 'mimes:jpg,jpeg,png,webp'],
'banner_file' => ['nullable', 'file', 'image', 'max:5120', 'mimes:jpg,jpeg,png,webp'],
];
}
}

View File

@@ -0,0 +1,28 @@
<?php
declare(strict_types=1);
namespace App\Http\Requests\Groups;
use Illuminate\Foundation\Http\FormRequest;
class UpdateGroupAssetRequest extends FormRequest
{
public function authorize(): bool
{
return $this->user() !== null;
}
public function rules(): array
{
return [
'title' => ['required', 'string', 'min:2', 'max:180'],
'description' => ['nullable', 'string', 'max:40000'],
'category' => ['nullable', 'in:' . implode(',', (array) config('groups.assets.categories', []))],
'visibility' => ['nullable', 'in:' . implode(',', (array) config('groups.assets.visibility_options', []))],
'status' => ['nullable', 'in:' . implode(',', (array) config('groups.assets.statuses', []))],
'linked_project_id' => ['nullable', 'integer'],
'is_featured' => ['nullable', 'boolean'],
];
}
}

View File

@@ -0,0 +1,9 @@
<?php
declare(strict_types=1);
namespace App\Http\Requests\Groups;
class UpdateGroupChallengeRequest extends StoreGroupChallengeRequest
{
}

View File

@@ -0,0 +1,9 @@
<?php
declare(strict_types=1);
namespace App\Http\Requests\Groups;
class UpdateGroupEventRequest extends StoreGroupEventRequest
{
}

View File

@@ -0,0 +1,26 @@
<?php
declare(strict_types=1);
namespace App\Http\Requests\Groups;
use App\Models\Group;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;
class UpdateGroupMemberPermissionsRequest extends FormRequest
{
public function authorize(): bool
{
return $this->user() !== null;
}
public function rules(): array
{
return [
'permission_overrides' => ['required', 'array'],
'permission_overrides.*.key' => ['required', 'string', Rule::in(Group::permissionKeys())],
'permission_overrides.*.is_allowed' => ['nullable', 'boolean'],
];
}
}

View File

@@ -0,0 +1,23 @@
<?php
declare(strict_types=1);
namespace App\Http\Requests\Groups;
use App\Models\Group;
use Illuminate\Foundation\Http\FormRequest;
class UpdateGroupMemberRequest extends FormRequest
{
public function authorize(): bool
{
return $this->user() !== null;
}
public function rules(): array
{
return [
'role' => ['required', 'in:' . implode(',', [Group::ROLE_ADMIN, Group::ROLE_EDITOR, Group::ROLE_MEMBER, Group::ROLE_CONTRIBUTOR])],
];
}
}

View File

@@ -0,0 +1,9 @@
<?php
declare(strict_types=1);
namespace App\Http\Requests\Groups;
class UpdateGroupMilestoneRequest extends StoreGroupMilestoneRequest
{
}

View File

@@ -0,0 +1,32 @@
<?php
declare(strict_types=1);
namespace App\Http\Requests\Groups;
use App\Models\GroupPost;
use Illuminate\Foundation\Http\FormRequest;
class UpdateGroupPostRequest extends FormRequest
{
public function authorize(): bool
{
return $this->user() !== null;
}
public function rules(): array
{
return [
'type' => ['sometimes', 'in:' . implode(',', [
GroupPost::TYPE_ANNOUNCEMENT,
GroupPost::TYPE_RELEASE,
GroupPost::TYPE_RECRUITMENT,
GroupPost::TYPE_UPDATE,
])],
'title' => ['sometimes', 'string', 'min:2', 'max:180'],
'excerpt' => ['nullable', 'string', 'max:320'],
'content' => ['nullable', 'string', 'max:40000'],
'cover_path' => ['nullable', 'string', 'max:2048'],
];
}
}

View File

@@ -0,0 +1,9 @@
<?php
declare(strict_types=1);
namespace App\Http\Requests\Groups;
class UpdateGroupProjectRequest extends StoreGroupProjectRequest
{
}

View File

@@ -0,0 +1,22 @@
<?php
declare(strict_types=1);
namespace App\Http\Requests\Groups;
use Illuminate\Foundation\Http\FormRequest;
class UpdateGroupProjectStatusRequest extends FormRequest
{
public function authorize(): bool
{
return $this->user() !== null;
}
public function rules(): array
{
return [
'status' => ['required', 'in:' . implode(',', (array) config('groups.projects.statuses', []))],
];
}
}

View File

@@ -0,0 +1,35 @@
<?php
declare(strict_types=1);
namespace App\Http\Requests\Groups;
use Illuminate\Foundation\Http\FormRequest;
class UpdateGroupRecruitmentRequest extends FormRequest
{
public function authorize(): bool
{
return $this->user() !== null;
}
public function rules(): array
{
$allowedRoles = config('groups.recruitment.roles', []);
$allowedSkills = config('groups.recruitment.skills', []);
$allowedContactModes = config('groups.recruitment.contact_modes', ['join_request', 'direct_message', 'external_link']);
$allowedVisibility = config('groups.recruitment.visibility_options', ['public', 'members_only', 'private']);
return [
'is_recruiting' => ['required', 'boolean'],
'headline' => ['nullable', 'string', 'max:180'],
'description' => ['nullable', 'string', 'max:4000'],
'roles_json' => ['nullable', 'array', 'max:12'],
'roles_json.*' => ['string', 'max:80', 'in:' . implode(',', $allowedRoles)],
'skills_json' => ['nullable', 'array', 'max:20'],
'skills_json.*' => ['string', 'max:80', 'in:' . implode(',', $allowedSkills)],
'contact_mode' => ['nullable', 'string', 'max:32', 'in:' . implode(',', $allowedContactModes)],
'visibility' => ['nullable', 'in:' . implode(',', $allowedVisibility)],
];
}
}

View File

@@ -0,0 +1,9 @@
<?php
declare(strict_types=1);
namespace App\Http\Requests\Groups;
class UpdateGroupReleaseRequest extends StoreGroupReleaseRequest
{
}

View File

@@ -0,0 +1,22 @@
<?php
declare(strict_types=1);
namespace App\Http\Requests\Groups;
use Illuminate\Foundation\Http\FormRequest;
class UpdateGroupReleaseStageRequest extends FormRequest
{
public function authorize(): bool
{
return $this->user() !== null;
}
public function rules(): array
{
return [
'current_stage' => ['required', 'in:' . implode(',', (array) config('groups.releases.stages', []))],
];
}
}

View File

@@ -0,0 +1,54 @@
<?php
declare(strict_types=1);
namespace App\Http\Requests\Groups;
use App\Models\Group;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Str;
class UpdateGroupRequest extends FormRequest
{
public function authorize(): bool
{
return $this->user() !== null;
}
protected function prepareForValidation(): void
{
$name = (string) $this->input('name', '');
$slug = (string) $this->input('slug', '');
if ($slug === '' && $name !== '') {
$slug = Str::slug(Str::limit($name, 90, ''));
}
$this->merge([
'slug' => $slug,
]);
}
public function rules(): array
{
return [
'name' => ['required', 'string', 'min:2', 'max:80'],
'slug' => ['required', 'string', 'min:2', 'max:90', 'regex:/^[a-z0-9]+(?:-[a-z0-9]+)*$/'],
'headline' => ['nullable', 'string', 'max:160'],
'bio' => ['nullable', 'string', 'max:3000'],
'visibility' => ['required', 'in:' . implode(',', Group::acceptedVisibilityValues())],
'membership_policy' => ['nullable', 'in:' . implode(',', Group::acceptedMembershipPolicies())],
'type' => ['nullable', 'string', 'max:80'],
'founded_at' => ['nullable', 'date'],
'website_url' => ['nullable', 'url', 'max:2048'],
'links_json' => ['nullable', 'array', 'max:8'],
'links_json.*.label' => ['required_with:links_json', 'string', 'max:40'],
'links_json.*.url' => ['required_with:links_json', 'url', 'max:2048'],
'avatar_path' => ['nullable', 'string', 'max:2048'],
'banner_path' => ['nullable', 'string', 'max:2048'],
'avatar_file' => ['nullable', 'file', 'image', 'max:5120', 'mimes:jpg,jpeg,png,webp'],
'banner_file' => ['nullable', 'file', 'image', 'max:5120', 'mimes:jpg,jpeg,png,webp'],
'featured_artwork_id' => ['nullable', 'integer', 'min:1'],
];
}
}