dev #5

Merged
RHPZAdmin merged 16 commits from dev into master 2026-06-24 10:13:34 +00:00
64 changed files with 2278 additions and 174 deletions
Showing only changes of commit 4f9f6c63b3 - Show all commits

View File

@@ -0,0 +1,48 @@
<?php
namespace App\Filament\Resources\Categories;
use App\Filament\Resources\Categories\Pages\CreateCategory;
use App\Filament\Resources\Categories\Pages\EditCategory;
use App\Filament\Resources\Categories\Pages\ListCategories;
use App\Filament\Resources\Categories\Schemas\CategoryForm;
use App\Filament\Resources\Categories\Tables\CategoriesTable;
use App\Models\Category;
use BackedEnum;
use Filament\Resources\Resource;
use Filament\Schemas\Schema;
use Filament\Support\Icons\Heroicon;
use Filament\Tables\Table;
class CategoryResource extends Resource
{
protected static ?string $model = Category::class;
protected static string|BackedEnum|null $navigationIcon = Heroicon::OutlinedRectangleStack;
public static function form(Schema $schema): Schema
{
return CategoryForm::configure($schema);
}
public static function table(Table $table): Table
{
return CategoriesTable::configure($table);
}
public static function getRelations(): array
{
return [
//
];
}
public static function getPages(): array
{
return [
'index' => ListCategories::route('/'),
'create' => CreateCategory::route('/create'),
'edit' => EditCategory::route('/{record}/edit'),
];
}
}

View File

@@ -0,0 +1,11 @@
<?php
namespace App\Filament\Resources\Categories\Pages;
use App\Filament\Resources\Categories\CategoryResource;
use Filament\Resources\Pages\CreateRecord;
class CreateCategory extends CreateRecord
{
protected static string $resource = CategoryResource::class;
}

View File

@@ -0,0 +1,19 @@
<?php
namespace App\Filament\Resources\Categories\Pages;
use App\Filament\Resources\Categories\CategoryResource;
use Filament\Actions\DeleteAction;
use Filament\Resources\Pages\EditRecord;
class EditCategory extends EditRecord
{
protected static string $resource = CategoryResource::class;
protected function getHeaderActions(): array
{
return [
DeleteAction::make(),
];
}
}

View File

@@ -0,0 +1,19 @@
<?php
namespace App\Filament\Resources\Categories\Pages;
use App\Filament\Resources\Categories\CategoryResource;
use Filament\Actions\CreateAction;
use Filament\Resources\Pages\ListRecords;
class ListCategories extends ListRecords
{
protected static string $resource = CategoryResource::class;
protected function getHeaderActions(): array
{
return [
CreateAction::make(),
];
}
}

View File

@@ -0,0 +1,24 @@
<?php
namespace App\Filament\Resources\Categories\Schemas;
use Filament\Forms\Components\TextInput;
use Filament\Forms\Components\Textarea;
use Filament\Schemas\Schema;
class CategoryForm
{
public static function configure(Schema $schema): Schema
{
return $schema
->components([
TextInput::make('name')
->required(),
TextInput::make('slug')
->required(),
Textarea::make('restricted_to')
->default(null)
->columnSpanFull(),
]);
}
}

View File

@@ -0,0 +1,42 @@
<?php
namespace App\Filament\Resources\Categories\Tables;
use Filament\Actions\BulkActionGroup;
use Filament\Actions\DeleteBulkAction;
use Filament\Actions\EditAction;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Table;
class CategoriesTable
{
public static function configure(Table $table): Table
{
return $table
->columns([
TextColumn::make('name')
->searchable(),
TextColumn::make('slug')
->searchable(),
TextColumn::make('created_at')
->dateTime()
->sortable()
->toggleable(isToggledHiddenByDefault: true),
TextColumn::make('updated_at')
->dateTime()
->sortable()
->toggleable(isToggledHiddenByDefault: true),
])
->filters([
//
])
->recordActions([
EditAction::make(),
])
->toolbarActions([
BulkActionGroup::make([
DeleteBulkAction::make(),
]),
]);
}
}

View File

@@ -0,0 +1,48 @@
<?php
namespace App\Filament\Resources\Levels;
use App\Filament\Resources\Levels\Pages\CreateLevel;
use App\Filament\Resources\Levels\Pages\EditLevel;
use App\Filament\Resources\Levels\Pages\ListLevels;
use App\Filament\Resources\Levels\Schemas\LevelForm;
use App\Filament\Resources\Levels\Tables\LevelsTable;
use App\Models\Level;
use BackedEnum;
use Filament\Resources\Resource;
use Filament\Schemas\Schema;
use Filament\Support\Icons\Heroicon;
use Filament\Tables\Table;
class LevelResource extends Resource
{
protected static ?string $model = Level::class;
protected static string|BackedEnum|null $navigationIcon = Heroicon::OutlinedRectangleStack;
public static function form(Schema $schema): Schema
{
return LevelForm::configure($schema);
}
public static function table(Table $table): Table
{
return LevelsTable::configure($table);
}
public static function getRelations(): array
{
return [
//
];
}
public static function getPages(): array
{
return [
'index' => ListLevels::route('/'),
'create' => CreateLevel::route('/create'),
'edit' => EditLevel::route('/{record}/edit'),
];
}
}

View File

@@ -0,0 +1,11 @@
<?php
namespace App\Filament\Resources\Levels\Pages;
use App\Filament\Resources\Levels\LevelResource;
use Filament\Resources\Pages\CreateRecord;
class CreateLevel extends CreateRecord
{
protected static string $resource = LevelResource::class;
}

View File

@@ -0,0 +1,19 @@
<?php
namespace App\Filament\Resources\Levels\Pages;
use App\Filament\Resources\Levels\LevelResource;
use Filament\Actions\DeleteAction;
use Filament\Resources\Pages\EditRecord;
class EditLevel extends EditRecord
{
protected static string $resource = LevelResource::class;
protected function getHeaderActions(): array
{
return [
DeleteAction::make(),
];
}
}

View File

@@ -0,0 +1,19 @@
<?php
namespace App\Filament\Resources\Levels\Pages;
use App\Filament\Resources\Levels\LevelResource;
use Filament\Actions\CreateAction;
use Filament\Resources\Pages\ListRecords;
class ListLevels extends ListRecords
{
protected static string $resource = LevelResource::class;
protected function getHeaderActions(): array
{
return [
CreateAction::make(),
];
}
}

View File

@@ -0,0 +1,20 @@
<?php
namespace App\Filament\Resources\Levels\Schemas;
use Filament\Forms\Components\TextInput;
use Filament\Schemas\Schema;
class LevelForm
{
public static function configure(Schema $schema): Schema
{
return $schema
->components([
TextInput::make('name')
->required(),
TextInput::make('slug')
->required(),
]);
}
}

View File

@@ -0,0 +1,42 @@
<?php
namespace App\Filament\Resources\Levels\Tables;
use Filament\Actions\BulkActionGroup;
use Filament\Actions\DeleteBulkAction;
use Filament\Actions\EditAction;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Table;
class LevelsTable
{
public static function configure(Table $table): Table
{
return $table
->columns([
TextColumn::make('name')
->searchable(),
TextColumn::make('slug')
->searchable(),
TextColumn::make('created_at')
->dateTime()
->sortable()
->toggleable(isToggledHiddenByDefault: true),
TextColumn::make('updated_at')
->dateTime()
->sortable()
->toggleable(isToggledHiddenByDefault: true),
])
->filters([
//
])
->recordActions([
EditAction::make(),
])
->toolbarActions([
BulkActionGroup::make([
DeleteBulkAction::make(),
]),
]);
}
}

View File

@@ -0,0 +1,11 @@
<?php
namespace App\Filament\Resources\Systems\Pages;
use App\Filament\Resources\Systems\SystemResource;
use Filament\Resources\Pages\CreateRecord;
class CreateSystem extends CreateRecord
{
protected static string $resource = SystemResource::class;
}

View File

@@ -0,0 +1,19 @@
<?php
namespace App\Filament\Resources\Systems\Pages;
use App\Filament\Resources\Systems\SystemResource;
use Filament\Actions\DeleteAction;
use Filament\Resources\Pages\EditRecord;
class EditSystem extends EditRecord
{
protected static string $resource = SystemResource::class;
protected function getHeaderActions(): array
{
return [
DeleteAction::make(),
];
}
}

View File

@@ -0,0 +1,19 @@
<?php
namespace App\Filament\Resources\Systems\Pages;
use App\Filament\Resources\Systems\SystemResource;
use Filament\Actions\CreateAction;
use Filament\Resources\Pages\ListRecords;
class ListSystems extends ListRecords
{
protected static string $resource = SystemResource::class;
protected function getHeaderActions(): array
{
return [
CreateAction::make(),
];
}
}

View File

@@ -0,0 +1,20 @@
<?php
namespace App\Filament\Resources\Systems\Schemas;
use Filament\Forms\Components\TextInput;
use Filament\Schemas\Schema;
class SystemForm
{
public static function configure(Schema $schema): Schema
{
return $schema
->components([
TextInput::make('name')
->required(),
TextInput::make('slug')
->required(),
]);
}
}

View File

@@ -0,0 +1,48 @@
<?php
namespace App\Filament\Resources\Systems;
use App\Filament\Resources\Systems\Pages\CreateSystem;
use App\Filament\Resources\Systems\Pages\EditSystem;
use App\Filament\Resources\Systems\Pages\ListSystems;
use App\Filament\Resources\Systems\Schemas\SystemForm;
use App\Filament\Resources\Systems\Tables\SystemsTable;
use App\Models\System;
use BackedEnum;
use Filament\Resources\Resource;
use Filament\Schemas\Schema;
use Filament\Support\Icons\Heroicon;
use Filament\Tables\Table;
class SystemResource extends Resource
{
protected static ?string $model = System::class;
protected static string|BackedEnum|null $navigationIcon = Heroicon::OutlinedRectangleStack;
public static function form(Schema $schema): Schema
{
return SystemForm::configure($schema);
}
public static function table(Table $table): Table
{
return SystemsTable::configure($table);
}
public static function getRelations(): array
{
return [
//
];
}
public static function getPages(): array
{
return [
'index' => ListSystems::route('/'),
'create' => CreateSystem::route('/create'),
'edit' => EditSystem::route('/{record}/edit'),
];
}
}

View File

@@ -0,0 +1,42 @@
<?php
namespace App\Filament\Resources\Systems\Tables;
use Filament\Actions\BulkActionGroup;
use Filament\Actions\DeleteBulkAction;
use Filament\Actions\EditAction;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Table;
class SystemsTable
{
public static function configure(Table $table): Table
{
return $table
->columns([
TextColumn::make('name')
->searchable(),
TextColumn::make('slug')
->searchable(),
TextColumn::make('created_at')
->dateTime()
->sortable()
->toggleable(isToggledHiddenByDefault: true),
TextColumn::make('updated_at')
->dateTime()
->sortable()
->toggleable(isToggledHiddenByDefault: true),
])
->filters([
//
])
->recordActions([
EditAction::make(),
])
->toolbarActions([
BulkActionGroup::make([
DeleteBulkAction::make(),
]),
]);
}
}

View File

@@ -44,7 +44,64 @@ class FormHelpers {
'homebrew' => [
'page_title' => "Submit an homebrew",
'about_the' => "About the homebrew",
'version' => "Patch version",
'version' => "Version",
'status' => "Status",
'release_date' => "Release date",
'release_date_helper' => "If only initial release exist, the release date.",
'description' => "Description",
'about_game' => "Game Information",
'attachments' => "Attachments",
'authors' => "Team members",
'related_links' => "Related links",
'release_site' => "Release site",
'release_site_helper' => "Project entry on site/blog/forum/Github.",
'youtube_video' => "YouTube video",
],
'utilities' => [
'page_title' => "Submit a utility",
'entry_title' => "Title",
'about_the' => "About the utility",
'version' => "Version",
'status' => "Status",
'system' => "OS",
'categories' => "Categories",
'level' => "Experience Level",
'release_date' => "Release date",
'release_date_helper' => "If only initial release exist, the release date.",
'description' => "Description",
'about_game' => "Game Information",
'attachments' => "Attachments",
'authors' => "Team members",
'related_links' => "Related links",
'release_site' => "Release site",
'release_site_helper' => "Project entry on site/blog/forum/Github.",
'youtube_video' => "YouTube video",
],
'documents' => [
'page_title' => "Submit a document",
'entry_title' => "Title",
'about_the' => "About the document",
'version' => "Version",
'status' => "Status",
'categories' => "Categories",
'level' => "Experience Level",
'release_date' => "Release date",
'release_date_helper' => "If only initial release exist, the release date.",
'description' => "Description",
'about_game' => "Game Information",
'attachments' => "Attachments",
'authors' => "Team members",
'related_links' => "Related links",
'release_site' => "Release site",
'release_site_helper' => "Project entry on site/blog/forum/Github.",
'youtube_video' => "YouTube video",
],
'lua-scripts' => [
'page_title' => "Submit a LUA Script",
'about_the' => "About the script",
'entry_title' => "Title",
'type_of_hack' => "Modifications",
'version' => "Version",
'status' => "Status",
'release_date' => "Release date",
'release_date_helper' => "If only initial release exist, the release date.",

View File

@@ -10,14 +10,16 @@ use App\Jobs\DeleteXenForoCommentsThread;
use App\Models\Author;
use App\Models\Entry;
use App\Models\EntryFile;
use App\Models\EntryGallery;
use App\Models\Gallery;
use App\Models\EntryHash;
use App\Models\Game;
use App\Models\Genre;
use App\Models\Language;
use App\Models\Level;
use App\Models\Modification;
use App\Models\Platform;
use App\Models\Status;
use App\Models\System;
use App\Services\SubmissionsService;
use App\Services\XenforoApiService;
use Illuminate\Http\Request;
@@ -39,19 +41,27 @@ class SubmissionController extends Controller
'words' => FormHelpers::getEntryFormWords($section),
'isEdit' => false,
'oldModifications' => old( 'modifications', [] ),
'oldSystems' => old( 'systems', [] ),
'oldLanguages' => old( 'languages', [] ),
'oldCategories' => old( 'categories', [] ),
'oldFilesArray' => $this->services->prepareOldFiles( null )
];
if( $data['words'] === [] )
abort(500);
if( section_must_be( 'romhacks', $section ) ){
if( section_must_be( ['romhacks', 'lua-scripts'], $section ) ){
$data['modifications'] = Modification::orderBy('name')->get();
}
if( section_must_be( [ 'romhacks', 'translations', 'homebrew' ], $section ) ){
if( section_must_be( [ 'romhacks', 'translations', 'homebrew', 'lua-scripts' ], $section ) ){
$data['statuses'] = Status::orderBy('id')->get();
}
if( section_must_be( 'utilities' , $section ) ){
$data['systems'] = System::orderBy('name')->get();
}
if( section_must_be( [ 'utilities', 'documents' ], $section ) ) {
$data['levels'] = Level::orderBy('id')->get();
}
return view('submissions.create', $data);
}
@@ -68,19 +78,27 @@ class SubmissionController extends Controller
'words' => FormHelpers::getEntryFormWords($section),
'isEdit' => true,
'oldModifications' => old('modifications', $entry->modifications->pluck('id')->toArray() ?? [] ),
'oldSystems' => old( 'systems', $entry->systems->pluck('id')->toArray() ?? [] ),
'oldLanguages' => old('languages', $entry->languages->pluck('id')->toArray() ?? [] ),
'oldCategories' => old('categories', $entry->categories->pluck('id')->toArray() ?? [] ),
'oldFilesArray' => $this->services->prepareOldFiles( $entry )
];
if( $data['words'] === [] )
abort(500);
if( section_must_be( 'romhacks', $section ) ){
if( section_must_be( [ 'romhacks', 'lua-scripts' ], $section ) ){
$data['modifications'] = Modification::orderBy('name')->get();
}
if( section_must_be( [ 'romhacks', 'translations' ], $section ) ){
if( section_must_be( [ 'romhacks', 'translations', 'homebrew', 'lua-scripts' ], $section ) ){
$data['statuses'] = Status::orderBy('id')->get();
}
if( section_must_be( 'utilities' , $section ) ){
$data['systems'] = System::orderBy('name')->get();
}
if( section_must_be( [ 'utilities', 'documents' ], $section ) ) {
$data['levels'] = Level::orderBy('id')->get();
}
return view('submissions.edit', $data);
}

View File

@@ -70,28 +70,49 @@ class StoreEntryRequest extends FormRequest
$rules['entry_title'] = "nullable|string|max:255";
}
if( section_must_be( 'romhacks', $section ) ){
if( section_must_be( ['romhacks', 'lua-scripts'], $section ) ){
$rules['modifications'] = 'array|required|min:1';
$rules['modifications.*'] = 'integer|exists:modifications,id';
} else if( section_must_be( 'utilities', $section ) ){
$rules['categories'] = 'array|required|min:1';
$rules['categories.*'] = 'integer|exists:categories,id';
}
if( section_must_be( 'utilities', $section ) ){
$rules['systems'] = 'array|required|min:1';
$rules['systems.*'] = 'integer|exists:systems,id';
}
$rules['version'] = 'required|string|max:50';
$rules['release-date'] = 'required|date';
$rules['status'] = 'required|integer|exists:statuses,id';
if( section_must_not_be( 'utilities', $section ) ){
$rules['status'] = 'required|integer|exists:statuses,id';
} else {
$rules['level'] = 'required|integer|exists:levels,id';
}
$rules['description'] = 'required|string';
if( section_must_be( ['romhacks', 'translations' ], $section ) ){
$rules['game_selection_mode'] = 'required|string|in:game,platform,none';
$gameSelectionMode = $this->input('game_selection_mode') !== '' ? $this->input('game_selection_mode') : 'game';
if( $gameSelectionMode === 'none' ){
// ...
} else if( $gameSelectionMode === 'platform' ){
$rules['platform_only_id'] = 'required|integer|exists:platforms,id';
} else {
$rules['game_id'] = 'required_without:new-game-title|nullable|integer|exists:games,id';
$rules['new-game-title'] = 'required_without:game_id|nullable|string|max:255';
$rules['new-game-platform'] = 'required_with:new-game-title|nullable|integer|exists:platforms,id';
$rules['new-game-genre'] = 'required_with:new-game-title|integer|nullable|exists:genres,id';
}
$rules['hashes'] = 'array|required|min:1';
$rules['hashes.*.filename'] = 'required|string|max:512';
$rules['hashes.*.hash_crc32'] = 'required|string|max:512';
$rules['hashes.*.hash_sha1'] = 'required|string|max:512';
$rules['hashes.*.verified'] = 'required|string|max:512';
if( section_must_be( ['translations', 'romhacks'], $section ) ){
$rules['hashes'] = 'array|required|min:1';
$rules['hashes.*.filename'] = 'required|string|max:512';
$rules['hashes.*.hash_crc32'] = 'required|string|max:512';
$rules['hashes.*.hash_sha1'] = 'required|string|max:512';
$rules['hashes.*.verified'] = 'required|string|max:512';
}
$rules['languages'] = 'array|required|min:1';
$rules['languages.*'] = 'integer|exists:languages,id';

View File

@@ -3,13 +3,16 @@
namespace App\Livewire;
use App\Models\Author;
use App\Models\Category;
use App\Models\Entry;
use App\Models\Game;
use App\Models\Genre;
use App\Models\Language;
use App\Models\Level;
use App\Models\Modification;
use App\Models\Platform;
use App\Models\Status;
use App\Models\System;
use Livewire\Attributes\Url;
use Livewire\Component;
use Livewire\WithPagination;
@@ -114,6 +117,41 @@ class Database extends Component
#[Url(except:'or')]
public string $modificationsMode = 'or';
/**
* Categories IDs filter.
* @var array
*/
#[Url(except:[])]
public array $categories = [];
/**
* Categories mode and/or
* @var string
*/
#[Url(except:['or'])]
public string $categoriesMode = 'or';
/**
* Systems IDs filter.
* @var array
*/
#[Url(except:[])]
public array $systems = [];
/**
* Systems mode and/or
* @var string
*/
#[Url(except:['or'])]
public string $systemsMode = 'or';
/**
* Levels IDs filter.
* @var array
*/
#[Url(except:[])]
public array $levels = [];
/**
* Sort by field.
* @var string
@@ -164,11 +202,16 @@ class Database extends Component
public function updatedLanguagesMode(): void { $this->resetPage(); $this->dispatch('filters-updated'); }
public function updatedModifications(): void { $this->resetPage(); $this->dispatch('filters-updated'); }
public function updatedModificationsMode(): void { $this->resetPage(); $this->dispatch('filters-updated'); }
public function updatedCategories(): void { $this->resetPage(); $this->dispatch('filters-updated'); }
public function updatedCategoriesMode(): void { $this->resetPage(); $this->dispatch('filters-updated'); }
public function updatedSystems(): void { $this->resetPage(); $this->dispatch('filters-updated'); }
public function updatedSystemsMode(): void { $this->resetPage(); $this->dispatch('filters-updated'); }
public function updatedLevels(): void { $this->resetPage(); $this->dispatch('filters-updated'); }
public function clearFilters(): void
{
$this->reset([
'search', 'types', 'platforms', 'genres', 'statuses', 'authors', 'authorsMode', 'languages', 'languagesMode', 'modifications', 'modificationsMode'
'search', 'types', 'platforms', 'genres', 'statuses', 'authors', 'authorsMode', 'languages', 'languagesMode', 'modifications', 'modificationsMode', 'categories', 'categoriesMode', 'systems', 'systemsMode', 'levels'
]);
$this->resetPage();
}
@@ -188,7 +231,7 @@ class Database extends Component
private function buildQuery()
{
$query = Entry::query()->published()->with([
'game.platform', 'game.genre', 'status', 'authors', 'languages'
'game.platform', 'game.genre', 'status', 'authors', 'languages', 'level', 'systems', 'categories', 'modifications'
]);
if( $this->search ) {
@@ -253,6 +296,30 @@ class Database extends Component
}
}
if( $this->levels ) {
$query->whereIn('level_id', $this->levels);
}
if( $this->categories ) {
if( $this->categoriesMode === 'and' ) {
foreach ( $this->categories as $categoryId ) {
$query->whereHas('categories', fn($q) => $q->where('categories.id', $categoryId));
}
} else {
$query->whereHas('categories', fn($q) => $q->whereIn('categories.id', $this->categories));
}
}
if( $this->systems ) {
if( $this->systemsMode === 'and' ) {
foreach ( $this->systems as $systemId ) {
$query->whereHas('systems', fn($q) => $q->where('systems.id', $systemId));
}
} else {
$query->whereHas('systems', fn($q) => $q->whereIn('systems.id', $this->systems));
}
}
return $query->orderBy($this->sortBy, $this->sortDir);
}
@@ -267,6 +334,9 @@ class Database extends Component
'allAuthors' => Author::orderBy('name')->get(),
'allLanguages' => Language::orderBy('name')->get(),
'allModifications' => Modification::orderBy('name')->get(),
'allCategories' => Category::orderBy('name')->get(),
'allLevels' => Level::orderBy('name')->get(),
'allSystems' => System::orderBy('name')->get(),
]);
}
}

View File

@@ -16,6 +16,17 @@ class GameSelector extends Component
public const int REQUIRED_CHARS = 3;
/**
* Which section we can change selection mode.
*/
public const array CHANGE_SECTION_MODE = [ 'utilities', 'documents' ];
/**
* Selection mode between game|platform|none.
* @var string
*/
public string $selectionMode = 'game';
/**
* If we are in new game mode.
* @var bool
@@ -70,9 +81,25 @@ class GameSelector extends Component
*/
public bool $dropdown = false;
public function mount( ?int $gameId = null, ?string $newGameTitle = null, ?int $newGamePlatform = null, ?int $newGameGenre = null ): void
/**
* In platform mode.
* @var int|null
*/
public ?int $platformModeId = null;
/**
* In platform mode.
* @var string|null
*/
public ?string $platformModeName = null;
public ?string $section = null;
public function mount( ?int $gameId = null, ?string $newGameTitle = null, ?int $newGamePlatform = null, ?int $newGameGenre = null, ?string $section, ?int $platformOnlyId ): void
{
$this->section = $section;
// If we selected an existent game.
if( $gameId ){
$game = Game::with(['platform','genre'])->find($gameId);
@@ -93,6 +120,36 @@ class GameSelector extends Component
$this->gamePlatformId = is_numeric($newGamePlatform) ? (int) $newGamePlatform : null;
$this->gameGenreId = is_numeric($newGameGenre) ? (int) $newGameGenre : null;
}
if( in_array( $section, self::CHANGE_SECTION_MODE ) ) {
if ($platformOnlyId) {
$this->selectionMode = 'platform';
$this->platformModeId = $platformOnlyId;
$platform = Platform::find($platformOnlyId);
if ($platform) {
$this->platformModeName = $platform->name;
} else {
$this->platformModeId = null;
}
}
}
}
public function setSelectionMode(string $mode): void
{
if( !in_array( $this->section, self::CHANGE_SECTION_MODE ) )
return;
$this->selectionMode = $mode;
if( $mode !== 'game' ){
$this->clearGame();
$this->newGame = false;
}
if( $mode !== 'platform' ){
$this->platformModeId = null;
$this->platformModeName = null;
}
}
/**
@@ -134,6 +191,15 @@ class GameSelector extends Component
}
public function selectPlatformOnly( int $id ): void
{
$platform = Platform::find($id);
if( $platform ){
$this->platformModeId = $platform->id;
$this->platformModeName = $platform->name;
}
}
/**
* Clear existent game selection.
* @return void
@@ -179,6 +245,10 @@ class GameSelector extends Component
}
$data['hasOldNewGame'] = old('new-game-title') || old('new-game-platform') || old('new-game-genre');
$data['canChangeSelection'] = in_array( $this->section, self::CHANGE_SECTION_MODE );
if( in_array( $this->section, self::CHANGE_SECTION_MODE ) )
$data['platforms'] = Platform::orderBy('name')->get();
return view('livewire.game-selector', $data );
}
}

View File

@@ -8,6 +8,28 @@ use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasMany;
/**
* @property int $id
* @property string $name
* @property string $slug
* @property string|null $website
* @property int|null $user_id
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Entry> $entries
* @property-read int|null $entries_count
* @method static \Illuminate\Database\Eloquent\Builder<static>|Author newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|Author newQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|Author query()
* @method static \Illuminate\Database\Eloquent\Builder<static>|Author whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Author whereId($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Author whereName($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Author whereSlug($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Author whereUpdatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Author whereUserId($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Author whereWebsite($value)
* @mixin \Eloquent
*/
class Author extends Model
{
protected $fillable = [

33
app/Models/Category.php Normal file
View File

@@ -0,0 +1,33 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
/**
* @property int $id
* @property string $name
* @property string $slug
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @property array<array-key, mixed>|null $restricted_to
* @method static \Illuminate\Database\Eloquent\Builder<static>|Category newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|Category newQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|Category query()
* @method static \Illuminate\Database\Eloquent\Builder<static>|Category whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Category whereId($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Category whereName($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Category whereRestrictedTo($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Category whereSlug($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Category whereUpdatedAt($value)
* @mixin \Eloquent
*/
class Category extends Model
{
protected $fillable = ['name', 'slug', 'restricted_to'];
protected $casts = [
'restricted_to' => 'array',
];
}

View File

@@ -2,17 +2,100 @@
namespace App\Models;
use App\Traits\HasGallery;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\SoftDeletes;
use Monolog\Level;
/**
* @property int $id
* @property string $type
* @property string|null $title
* @property string|null $slug
* @property string|null $description
* @property string|null $main_image
* @property string $state
* @property string|null $staff_comment
* @property \Illuminate\Support\Carbon|null $rejected_at
* @property bool $featured
* @property int|null $game_id
* @property int|null $platform_id
* @property int|null $status_id
* @property string|null $version
* @property \Illuminate\Support\Carbon|null $release_date
* @property string|null $staff_credits
* @property string|null $relevant_link
* @property string|null $youtube_link
* @property int $user_id
* @property int|null $comments_thread_id
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @property string|null $complete_title
* @property \Illuminate\Support\Carbon|null $deleted_at
* @property int|null $level_id
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Author> $authors
* @property-read int|null $authors_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Category> $categories
* @property-read int|null $categories_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\EntryFile> $files
* @property-read int|null $files_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Gallery> $gallery
* @property-read int|null $gallery_count
* @property-read \App\Models\Game|null $game
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\EntryHash> $hashes
* @property-read int|null $hashes_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Language> $languages
* @property-read int|null $languages_count
* @property-read \App\Models\Level|null $level
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Modification> $modifications
* @property-read int|null $modifications_count
* @property-read \App\Models\Platform|null $platform
* @property-read \App\Models\Status|null $status
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\System> $systems
* @property-read int|null $systems_count
* @method static Builder<static>|Entry inQueue(int $daysRejected = 7)
* @method static Builder<static>|Entry newModelQuery()
* @method static Builder<static>|Entry newQuery()
* @method static Builder<static>|Entry onlyTrashed()
* @method static Builder<static>|Entry published()
* @method static Builder<static>|Entry query()
* @method static Builder<static>|Entry whereCommentsThreadId($value)
* @method static Builder<static>|Entry whereCompleteTitle($value)
* @method static Builder<static>|Entry whereCreatedAt($value)
* @method static Builder<static>|Entry whereDeletedAt($value)
* @method static Builder<static>|Entry whereDescription($value)
* @method static Builder<static>|Entry whereFeatured($value)
* @method static Builder<static>|Entry whereGameId($value)
* @method static Builder<static>|Entry whereId($value)
* @method static Builder<static>|Entry whereLevelId($value)
* @method static Builder<static>|Entry whereMainImage($value)
* @method static Builder<static>|Entry wherePlatformId($value)
* @method static Builder<static>|Entry whereRejectedAt($value)
* @method static Builder<static>|Entry whereReleaseDate($value)
* @method static Builder<static>|Entry whereRelevantLink($value)
* @method static Builder<static>|Entry whereSlug($value)
* @method static Builder<static>|Entry whereStaffComment($value)
* @method static Builder<static>|Entry whereStaffCredits($value)
* @method static Builder<static>|Entry whereState($value)
* @method static Builder<static>|Entry whereStatusId($value)
* @method static Builder<static>|Entry whereTitle($value)
* @method static Builder<static>|Entry whereType($value)
* @method static Builder<static>|Entry whereUpdatedAt($value)
* @method static Builder<static>|Entry whereUserId($value)
* @method static Builder<static>|Entry whereVersion($value)
* @method static Builder<static>|Entry whereYoutubeLink($value)
* @method static Builder<static>|Entry withTrashed(bool $withTrashed = true)
* @method static Builder<static>|Entry withoutTrashed()
* @mixin \Eloquent
*/
class Entry extends Model
{
use SoftDeletes;
use SoftDeletes, HasGallery;
/**
* @var string[]
@@ -38,6 +121,7 @@ class Entry extends Model
'comments_thread_id',
'staff_comment',
'rejected_at',
'level_id'
];
/**
@@ -81,6 +165,10 @@ class Entry extends Model
return $this->belongsTo(Status::class );
}
public function level(): BelongsTo {
return $this->belongsTo(\App\Models\Level::class);
}
public function authors(): BelongsToMany {
return $this->belongsToMany(Author::class, 'entry_authors');
}
@@ -93,12 +181,16 @@ class Entry extends Model
return $this->belongsToMany( Modification::class, 'entry_modifications');
}
public function files(): HasMany {
return $this->hasMany(EntryFile::class)->orderBy('filename');
public function categories(): BelongsToMany {
return $this->belongsToMany(Category::class, 'entry_categories');
}
public function gallery(): HasMany {
return $this->hasMany(EntryGallery::class)->orderBy('id');
public function systems(): BelongsToMany {
return $this->belongsToMany(System::class, 'entry_systems');
}
public function files(): HasMany {
return $this->hasMany(EntryFile::class)->orderBy('filename');
}
public function hashes(): HasMany {

View File

@@ -5,6 +5,39 @@ namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
/**
* @property int $id
* @property int $entry_id
* @property string $filename
* @property string $filepath
* @property string $favorite_server
* @property int $favorite_at
* @property int|null $filesize
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @property string $file_uuid
* @property string $state
* @property int $online_patcher
* @property int $secondary_online_patcher
* @property-read \App\Models\Entry|null $entry
* @method static \Illuminate\Database\Eloquent\Builder<static>|EntryFile newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|EntryFile newQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|EntryFile query()
* @method static \Illuminate\Database\Eloquent\Builder<static>|EntryFile whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|EntryFile whereEntryId($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|EntryFile whereFavoriteAt($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|EntryFile whereFavoriteServer($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|EntryFile whereFileUuid($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|EntryFile whereFilename($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|EntryFile whereFilepath($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|EntryFile whereFilesize($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|EntryFile whereId($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|EntryFile whereOnlinePatcher($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|EntryFile whereSecondaryOnlinePatcher($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|EntryFile whereState($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|EntryFile whereUpdatedAt($value)
* @mixin \Eloquent
*/
class EntryFile extends Model
{
protected $fillable = [

View File

@@ -2,10 +2,7 @@
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class EntryGallery extends Model
{
protected $fillable = ['entry_id','image'];
}
/**
* @deprecated Use Gallery instead.
*/
class EntryGallery extends Gallery {}

View File

@@ -5,6 +5,29 @@ namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
/**
* @property int $id
* @property int $entry_id
* @property string $filename
* @property string $hash_crc32
* @property string $hash_sha1
* @property string $verified
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @property-read \App\Models\Entry|null $entry
* @method static \Illuminate\Database\Eloquent\Builder<static>|EntryHash newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|EntryHash newQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|EntryHash query()
* @method static \Illuminate\Database\Eloquent\Builder<static>|EntryHash whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|EntryHash whereEntryId($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|EntryHash whereFilename($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|EntryHash whereHashCrc32($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|EntryHash whereHashSha1($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|EntryHash whereId($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|EntryHash whereUpdatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|EntryHash whereVerified($value)
* @mixin \Eloquent
*/
class EntryHash extends Model
{
protected $fillable = [

34
app/Models/Gallery.php Normal file
View File

@@ -0,0 +1,34 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\MorphTo;
/**
* @property int $id
* @property int $entry_id
* @property string $image
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @method static \Illuminate\Database\Eloquent\Builder<static>|Gallery newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|Gallery newQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|Gallery query()
* @method static \Illuminate\Database\Eloquent\Builder<static>|Gallery whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Gallery whereEntryId($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Gallery whereId($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Gallery whereImage($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Gallery whereUpdatedAt($value)
* @mixin \Eloquent
*/
class Gallery extends Model
{
protected $table = 'galleries';
protected $fillable = ['image', 'galleryable_id', 'galleryable_type'];
public function galleryable(): MorphTo
{
return $this->morphTo();
}
}

View File

@@ -5,6 +5,30 @@ namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
/**
* @property int $id
* @property string $name
* @property string $slug
* @property int $platform_id
* @property int $genre_id
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Entry> $entries
* @property-read int|null $entries_count
* @property-read \App\Models\Genre $genre
* @property-read \App\Models\Platform $platform
* @method static \Illuminate\Database\Eloquent\Builder<static>|Game newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|Game newQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|Game query()
* @method static \Illuminate\Database\Eloquent\Builder<static>|Game whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Game whereGenreId($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Game whereId($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Game whereName($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Game wherePlatformId($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Game whereSlug($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Game whereUpdatedAt($value)
* @mixin \Eloquent
*/
class Game extends Model
{
/**

View File

@@ -6,6 +6,24 @@ use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasMany;
/**
* @property int $id
* @property string $name
* @property string $slug
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Game> $games
* @property-read int|null $games_count
* @method static \Illuminate\Database\Eloquent\Builder<static>|Genre newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|Genre newQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|Genre query()
* @method static \Illuminate\Database\Eloquent\Builder<static>|Genre whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Genre whereId($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Genre whereName($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Genre whereSlug($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Genre whereUpdatedAt($value)
* @mixin \Eloquent
*/
class Genre extends Model
{
protected $fillable = [ 'name', 'slug' ];

View File

@@ -6,6 +6,24 @@ use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasMany;
/**
* @property int $id
* @property string $name
* @property string $slug
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Entry> $entries
* @property-read int|null $entries_count
* @method static \Illuminate\Database\Eloquent\Builder<static>|Language newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|Language newQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|Language query()
* @method static \Illuminate\Database\Eloquent\Builder<static>|Language whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Language whereId($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Language whereName($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Language whereSlug($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Language whereUpdatedAt($value)
* @mixin \Eloquent
*/
class Language extends Model
{
protected $fillable = [ 'name', 'slug' ];

26
app/Models/Level.php Normal file
View File

@@ -0,0 +1,26 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
/**
* @property int $id
* @property string $name
* @property string $slug
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @method static \Illuminate\Database\Eloquent\Builder<static>|Level newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|Level newQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|Level query()
* @method static \Illuminate\Database\Eloquent\Builder<static>|Level whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Level whereId($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Level whereName($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Level whereSlug($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Level whereUpdatedAt($value)
* @mixin \Eloquent
*/
class Level extends Model
{
protected $fillable = ['name', 'slug'];
}

View File

@@ -4,6 +4,22 @@ namespace App\Models;
use Illuminate\Database\Eloquent\Model;
/**
* @property int $id
* @property string $name
* @property string $slug
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @method static \Illuminate\Database\Eloquent\Builder<static>|Modification newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|Modification newQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|Modification query()
* @method static \Illuminate\Database\Eloquent\Builder<static>|Modification whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Modification whereId($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Modification whereName($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Modification whereSlug($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Modification whereUpdatedAt($value)
* @mixin \Eloquent
*/
class Modification extends Model
{
protected $fillable = [ 'name', 'slug' ];

View File

@@ -5,6 +5,28 @@ namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
/**
* @property int $id
* @property string $name
* @property string $slug
* @property string|null $short_name
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Entry> $entries
* @property-read int|null $entries_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Game> $games
* @property-read int|null $games_count
* @method static \Illuminate\Database\Eloquent\Builder<static>|Platform newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|Platform newQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|Platform query()
* @method static \Illuminate\Database\Eloquent\Builder<static>|Platform whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Platform whereId($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Platform whereName($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Platform whereShortName($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Platform whereSlug($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Platform whereUpdatedAt($value)
* @mixin \Eloquent
*/
class Platform extends Model
{

View File

@@ -4,6 +4,22 @@ namespace App\Models;
use Illuminate\Database\Eloquent\Model;
/**
* @property int $id
* @property string $name
* @property string $slug
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @method static \Illuminate\Database\Eloquent\Builder<static>|Status newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|Status newQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|Status query()
* @method static \Illuminate\Database\Eloquent\Builder<static>|Status whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Status whereId($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Status whereName($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Status whereSlug($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Status whereUpdatedAt($value)
* @mixin \Eloquent
*/
class Status extends Model
{
protected $fillable = ['name', 'slug'];

26
app/Models/System.php Normal file
View File

@@ -0,0 +1,26 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
/**
* @property int $id
* @property string $name
* @property string $slug
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @method static \Illuminate\Database\Eloquent\Builder<static>|System newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|System newQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|System query()
* @method static \Illuminate\Database\Eloquent\Builder<static>|System whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|System whereId($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|System whereName($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|System whereSlug($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|System whereUpdatedAt($value)
* @mixin \Eloquent
*/
class System extends Model
{
protected $fillable = ['name', 'slug'];
}

View File

@@ -10,6 +10,31 @@ use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
/**
* @property int $id
* @property string $name
* @property string $email
* @property \Illuminate\Support\Carbon|null $email_verified_at
* @property string $password
* @property string|null $remember_token
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @property-read \Illuminate\Notifications\DatabaseNotificationCollection<int, \Illuminate\Notifications\DatabaseNotification> $notifications
* @property-read int|null $notifications_count
* @method static \Database\Factories\UserFactory factory($count = null, $state = [])
* @method static \Illuminate\Database\Eloquent\Builder<static>|User newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|User newQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|User query()
* @method static \Illuminate\Database\Eloquent\Builder<static>|User whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|User whereEmail($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|User whereEmailVerifiedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|User whereId($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|User whereName($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|User wherePassword($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|User whereRememberToken($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|User whereUpdatedAt($value)
* @mixin \Eloquent
*/
#[Fillable(['name', 'email', 'password'])]
#[Hidden(['password', 'remember_token'])]
class User extends Authenticatable

View File

@@ -8,15 +8,17 @@ use App\Helpers\XenForoHelpers;
use App\Http\Requests\StoreEntryRequest;
use App\Jobs\CreateXenForoCommentsThread;
use App\Models\Author;
use App\Models\Category;
use App\Models\Entry;
use App\Models\EntryFile;
use App\Models\EntryGallery;
use App\Models\Gallery;
use App\Models\EntryHash;
use App\Models\Game;
use App\Models\Genre;
use App\Models\Language;
use App\Models\Modification;
use App\Models\Platform;
use App\Models\System;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
@@ -118,11 +120,7 @@ class SubmissionsService {
$entry = DB::transaction(function () use ( $user_id ) {
// STEP 2 : Create game.
$gameId = null;
if( section_must_be( ['romhacks', 'translations'], $this->section ) ){
$gameId = $this->Step2_CreateAndReturnGameId();
}
$gameId = $this->Step2_CreateAndReturnGameId();
// STEP 3 : Create Complete title.
$completeTitle = $this->Step3_BuildCompleteTitle( $gameId );
@@ -132,7 +130,7 @@ class SubmissionsService {
if( section_must_be( 'translations', $this->section ) &&
!$this->request->input('entry_title') ){
$entryTitle = Game::find($gameId)->name;
$entryTitle = Game::find($gameId)->name ?? "";
} else {
$entryTitle = $this->request->input('entry_title');
}
@@ -149,6 +147,7 @@ class SubmissionsService {
'main_image' => $this->request->input('main-image'),
'state' => $this->request->input('submit-state'),
'game_id' => $gameId,
'platform_id' => $this->request->input('platform_only_id'),
'status_id' => $this->request->input('status'),
'version' => $this->request->input('version'),
'release_date' => $this->request->input('release-date'),
@@ -157,6 +156,7 @@ class SubmissionsService {
'youtube_link' => $this->request->input('youtube_video'),
'user_id' => $user_id,
'complete_title' => $completeTitle,
'level_id' => $this->request->input('level')
];
$entry = Entry::create( $fields );
@@ -165,19 +165,28 @@ class SubmissionsService {
$this->Step7_SaveEntryFiles( $entry );
// STEP 8 : Save hashes.
$this->Step8_SaveHashes( $entry->id );
if( section_must_be( ['translations', 'romhacks' ], $this->section ) )
$this->Step8_SaveHashes( $entry->id );
// STEP 9 : Save Authors.
$this->Step9_SaveAuthors( $entry );
// STEP 10 : Save Modifications.
if( section_must_be( 'romhacks', $this->section ) ){
if( section_must_be( ['romhacks','lua-scripts'], $this->section ) ){
$this->Step10_SaveRomhacksModifications( $entry );
}
if( section_must_be( 'utilities', $this->section ) ){
$this->Step10_SaveUtilitiesSystems( $entry );
}
// STEP 11 : Save Languages
$this->Step11_SaveLanguages( $entry );
// STEP 11.5 : Save Categories
if( section_must_be( 'utilities', $this->section ) ) {
$this->Step11_5_SaveCategories($entry);
}
// STEP 12 : Prepare Gallery images.
$this->Step12a_PrepareGalleryImages( $entry );
@@ -207,6 +216,10 @@ class SubmissionsService {
*/
private function Step2_CreateAndReturnGameId(): ?int {
$mode = $this->request->input('game_selection_mode', 'game');
if( $mode !== 'game' )
return null;
// Already existing game.
if( $this->request->input('game_id') )
return $this->request->input('game_id');
@@ -261,9 +274,9 @@ class SubmissionsService {
if( section_must_be( 'translations', $this->section ) ) {
$fields['languages_string'] = Language::whereIn('id', $this->request->input('languages', []))->pluck('name')->implode(', ');
}
if( section_must_be(['romhacks', 'translations', 'homebrew', 'lua-scripts', 'tutorials'], $this->section ) ) {
if( section_must_be(['romhacks', 'translations', 'homebrew', 'lua-scripts'], $this->section ) ) {
// TODO: Add single platform ID compatibility.
$fields['platform_name'] = $gameId ? Game::find( $gameId )->platform->name : null;
$fields['platform_name'] = $gameId ? Game::find( $gameId )->platform->name : Platform::find( $this->request->input('platform_only_id') )?->name ?? null;
}
return EntryHelpers::buildCompleteTitle( $this->section, $fields );
@@ -381,6 +394,24 @@ class SubmissionsService {
}
}
/**
* @param Entry $entry
*
* @return void
* @throws SubmissionException
*/
private function Step10_SaveUtilitiesSystems( Entry $entry ): void
{
// TODO: Replace by edit version
foreach ( $this->request->input('systems', [] ) ?? [] as $systemId ) {
$system = System::find( $systemId );
if( !$system )
throw new SubmissionException( "System {$systemId} does not exist." );
$entry->systems()->attach( $system->id );
}
}
/**
* @param Entry $entry
*
@@ -400,11 +431,29 @@ class SubmissionsService {
}
/**
* @param Entry $entry
*
* @return void
* @throws SubmissionException
*/
private function Step11_5_SaveCategories( Entry $entry ): void
{
// TODO: Replace by edit version.
foreach ( $this->request->input('categories', [] ) ?? [] as $categoryId ) {
$category = Category::find( $categoryId );
if( !$category )
throw new SubmissionException( "Category {$categoryId} does not exist." );
$entry->categories()->attach( $category->id );
}
}
private function Step12a_PrepareGalleryImages( Entry $entry ): void
{
foreach ( $this->request->input('gallery', [] ) ?? [] as $imagePath ) {
EntryGallery::create([
'entry_id' => $entry->id,
$entry->gallery()->create([
'image' => $imagePath,
]);
}
@@ -464,9 +513,7 @@ class SubmissionsService {
// STEP 2: Create game if different.
$gameId = null;
if( section_must_be( ['romhacks', 'translations' ], $this->section ) ){
$gameId = $this->eStep2_VerifyCreateAndEditGameId();
}
$gameId = $this->eStep2_VerifyCreateAndEditGameId();
// STEP 3: Recreate complete title and refresh slug if needed.
$completeTitle = $this->Step3_BuildCompleteTitle( $gameId );
@@ -494,6 +541,7 @@ class SubmissionsService {
'main_image' => $this->request->input('main-image'),
'state' => $this->request->input('submit-state'),
'game_id' => $gameId,
'platform_id' => $this->request->input('platform_only_id'),
'status_id' => $this->request->input('status'),
'version' => $this->request->input('version'),
'release_date' => $this->request->input('release-date'),
@@ -502,12 +550,13 @@ class SubmissionsService {
'youtube_link' => $this->request->input('youtube_video'),
'user_id' => $user_id,
'complete_title' => $completeTitle,
'comments_thread_id' => $this->request->input('comments_thread_id'),
'featured' => $this->request->input('featured') ?? false,
'level_id' => $this->request->input('level'),
];
if( \Auth::user()->can('moderate', $this->entry) ){
$fields['staff_comment'] = $this->request->input('staff_comment');
$fields['featured'] = $this->request->input('featured') ?? false;
$fields['comments_thread_id'] = $this->request->input('comments_thread_id');
}
$this->entry->update( $fields );
@@ -516,19 +565,27 @@ class SubmissionsService {
$this->eStep6_UpdateEntryFiles( $this->entry->id );
// STEP 7: Update hashes.
$this->eStep7_UpdateHashes( $this->entry->id );
if( section_must_be( ['translations', 'romhacks' ], $this->section ) )
$this->eStep7_UpdateHashes( $this->entry->id );
// STEP 8: Update Authors.
$this->eStep8_UpdateAuthors();
// STEP 9: Update romhacks modifications.
if( section_must_be( 'romhacks', $this->section ) ) {
if( section_must_be( ['romhacks', 'lua-scripts'], $this->section ) ) {
$this->eStep9_UpdateRomhacksModifications();
}
if( section_must_be( 'utilities', $this->section ) ) {
$this->eStep9_UpdateUtilitiesSystems();
}
// STEP 10: Update Languages.
$this->eStep10_UpdateLanguages();
// STEP 10.5 : Update categories
if( section_must_be( 'utilities', $this->section ) )
$this->eStep10_5_UpdateCategories();
// STEP 11: Prepare new gallery images and prepare deletion of others ones.
$galleryPaths = $this->eStep11a_UpdateGalleryImages();
@@ -558,8 +615,14 @@ class SubmissionsService {
/**
* @throws SubmissionException
*/
private function eStep2_VerifyCreateAndEditGameId(): int
private function eStep2_VerifyCreateAndEditGameId(): ?int
{
$mode = $this->request->input('game_selection_mode', 'game');
if ($mode !== 'game') {
return null;
}
// Already existing game.
if( $this->request->input('game_id') ){
@@ -721,6 +784,27 @@ class SubmissionsService {
}
/**
* @return void
* @throws SubmissionException
*/
private function eStep9_UpdateUtilitiesSystems(): void
{
$requestSystems = $this->request->input('systems', [] ) ?? [];
if( !empty( $requestSystems ) ){
$valid = System::whereIn( 'id', $requestSystems )->pluck('id')->toArray();
if( count( $valid ) !== count( $requestSystems ) ){
throw new SubmissionException( "One of the systems doesn't exist." );
}
}
$this->entry->systems()->sync( $requestSystems );
}
/**
* @return void
* @throws SubmissionException
@@ -739,6 +823,24 @@ class SubmissionsService {
$this->entry->languages()->sync( $requestLanguages );
}
/**
* @return void
* @throws SubmissionException
*/
private function eStep10_5_UpdateCategories(): void
{
$requestCategories = $this->request->input('categories', [] ) ?? [];
if( !empty( $requestCategories ) ){
$valid = Category::whereIn( 'id', $requestCategories )->pluck('id')->toArray();
if( count( $valid ) !== count( $requestCategories ) ){
throw new SubmissionException( "One of the categories doesn't exist." );
}
}
$this->entry->categories()->sync( $requestCategories );
}
private function eStep11a_UpdateGalleryImages(): array
{
$requestGallery = $this->request->input('gallery', [] ) ?? [];
@@ -747,14 +849,13 @@ class SubmissionsService {
$needDeletion = array_diff( $existingGalleryPaths, $requestGallery );
if( !empty( $needDeletion ) ){
EntryGallery::where('entry_id', $this->entry->id)->whereIn('image', $needDeletion )->delete();
$this->entry->gallery()->whereIn('image', $needDeletion )->delete();
}
$needAddition = array_diff( $requestGallery, $existingGalleryPaths );
$images = [];
foreach( $needAddition as $imagePath ){
$images[] = EntryGallery::create([
'entry_id' => $this->entry->id,
$images[] = $this->entry->gallery()->create([
'image' => $imagePath,
]);
}

14
app/Traits/HasGallery.php Normal file
View File

@@ -0,0 +1,14 @@
<?php
namespace App\Traits;
use App\Models\Gallery;
use Illuminate\Database\Eloquent\Relations\MorphMany;
trait HasGallery
{
public function gallery(): MorphMany
{
return $this->morphMany(Gallery::class, 'galleryable')->orderBy('id');
}
}

View File

@@ -0,0 +1,41 @@
<?php
namespace App\View\Components;
use App\Models\Category;
use App\Models\Language;
use Closure;
use Illuminate\Contracts\View\View;
use Illuminate\View\Component;
class CategorySelector extends Component
{
public $categories;
/**f
* Create a new component instance.
*/
public function __construct(
public string $section,
public array $selected = [],
public bool $required = true
)
{
$this->categories = Category::query()
->where(function ($query) {
$query->whereJsonContains('restricted_to', $this->section)
->orWhereNull('restricted_to');
})
->orderBy('name')
->get();
}
/**
* Get the view / contents that represent the component.
*/
public function render(): View|Closure|string
{
return view('components.category-selector');
}
}

View File

@@ -18,6 +18,7 @@
},
"require-dev": {
"barryvdh/laravel-debugbar": "^4.2",
"barryvdh/laravel-ide-helper": "^3.7",
"fakerphp/faker": "^1.23",
"larastan/larastan": "^3.9",
"laravel/pail": "^1.2.5",

301
composer.lock generated
View File

@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "a1c836758aaea45cc09c0c7786ea366f",
"content-hash": "79ce85f4c121f30d964f9322cb7120a4",
"packages": [
{
"name": "blade-ui-kit/blade-heroicons",
@@ -8316,6 +8316,301 @@
],
"time": "2026-04-20T13:31:29+00:00"
},
{
"name": "barryvdh/laravel-ide-helper",
"version": "v3.7.0",
"source": {
"type": "git",
"url": "https://github.com/barryvdh/laravel-ide-helper.git",
"reference": "ad7e37676f1ff985d55ef1b6b96a0c0a40f2609a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/barryvdh/laravel-ide-helper/zipball/ad7e37676f1ff985d55ef1b6b96a0c0a40f2609a",
"reference": "ad7e37676f1ff985d55ef1b6b96a0c0a40f2609a",
"shasum": ""
},
"require": {
"barryvdh/reflection-docblock": "^2.4",
"composer/class-map-generator": "^1.0",
"ext-json": "*",
"illuminate/console": "^11.15 || ^12 || ^13.0",
"illuminate/database": "^11.15 || ^12 || ^13.0",
"illuminate/filesystem": "^11.15 || ^12 || ^13.0",
"illuminate/support": "^11.15 || ^12 || ^13.0",
"php": "^8.2"
},
"require-dev": {
"ext-pdo_sqlite": "*",
"friendsofphp/php-cs-fixer": "^3",
"illuminate/config": "^11.15 || ^12 || ^13.0",
"illuminate/view": "^11.15 || ^12 || ^13.0",
"larastan/larastan": "^3.1",
"mockery/mockery": "^1.4",
"orchestra/testbench": "^9.2 || ^10 || ^11.0",
"phpstan/phpstan-phpunit": "^2.0",
"phpunit/phpunit": "^10.5 || ^11.5.3 || ^12.5.12",
"spatie/phpunit-snapshot-assertions": "^4 || ^5",
"vlucas/phpdotenv": "^5"
},
"suggest": {
"illuminate/events": "Required for automatic helper generation (^6|^7|^8|^9|^10|^11)."
},
"type": "library",
"extra": {
"laravel": {
"providers": [
"Barryvdh\\LaravelIdeHelper\\IdeHelperServiceProvider"
]
},
"branch-alias": {
"dev-master": "3.6-dev"
}
},
"autoload": {
"psr-4": {
"Barryvdh\\LaravelIdeHelper\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Barry vd. Heuvel",
"email": "barryvdh@gmail.com"
}
],
"description": "Laravel IDE Helper, generates correct PHPDocs for all Facade classes, to improve auto-completion.",
"keywords": [
"autocomplete",
"codeintel",
"dev",
"helper",
"ide",
"laravel",
"netbeans",
"phpdoc",
"phpstorm",
"sublime"
],
"support": {
"issues": "https://github.com/barryvdh/laravel-ide-helper/issues",
"source": "https://github.com/barryvdh/laravel-ide-helper/tree/v3.7.0"
},
"funding": [
{
"url": "https://fruitcake.nl",
"type": "custom"
},
{
"url": "https://github.com/barryvdh",
"type": "github"
}
],
"time": "2026-03-17T14:12:51+00:00"
},
{
"name": "barryvdh/reflection-docblock",
"version": "v2.4.1",
"source": {
"type": "git",
"url": "https://github.com/barryvdh/ReflectionDocBlock.git",
"reference": "4f5ba70c30c81f2ce03a16a9965832cfcc31ed3b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/barryvdh/ReflectionDocBlock/zipball/4f5ba70c30c81f2ce03a16a9965832cfcc31ed3b",
"reference": "4f5ba70c30c81f2ce03a16a9965832cfcc31ed3b",
"shasum": ""
},
"require": {
"php": ">=7.1"
},
"require-dev": {
"phpunit/phpunit": "^8.5.14|^9"
},
"suggest": {
"dflydev/markdown": "~1.0",
"erusev/parsedown": "~1.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.3.x-dev"
}
},
"autoload": {
"psr-0": {
"Barryvdh": [
"src/"
]
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Mike van Riel",
"email": "mike.vanriel@naenius.com"
}
],
"support": {
"source": "https://github.com/barryvdh/ReflectionDocBlock/tree/v2.4.1"
},
"time": "2026-03-05T20:09:01+00:00"
},
{
"name": "composer/class-map-generator",
"version": "1.7.3",
"source": {
"type": "git",
"url": "https://github.com/composer/class-map-generator.git",
"reference": "86d8208fc3c649a3a999daf1a63c25201be2990f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/composer/class-map-generator/zipball/86d8208fc3c649a3a999daf1a63c25201be2990f",
"reference": "86d8208fc3c649a3a999daf1a63c25201be2990f",
"shasum": ""
},
"require": {
"composer/pcre": "^2.1 || ^3.1",
"php": "^7.2 || ^8.0",
"symfony/finder": "^4.4 || ^5.3 || ^6 || ^7 || ^8"
},
"require-dev": {
"phpstan/phpstan": "^1.12 || ^2",
"phpstan/phpstan-deprecation-rules": "^1 || ^2",
"phpstan/phpstan-phpunit": "^1 || ^2",
"phpstan/phpstan-strict-rules": "^1.1 || ^2",
"phpunit/phpunit": "^8",
"symfony/filesystem": "^5.4 || ^6 || ^7 || ^8"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "1.x-dev"
}
},
"autoload": {
"psr-4": {
"Composer\\ClassMapGenerator\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Jordi Boggiano",
"email": "j.boggiano@seld.be",
"homepage": "https://seld.be"
}
],
"description": "Utilities to scan PHP code and generate class maps.",
"keywords": [
"classmap"
],
"support": {
"issues": "https://github.com/composer/class-map-generator/issues",
"source": "https://github.com/composer/class-map-generator/tree/1.7.3"
},
"funding": [
{
"url": "https://packagist.com",
"type": "custom"
},
{
"url": "https://github.com/composer",
"type": "github"
}
],
"time": "2026-05-05T09:17:07+00:00"
},
{
"name": "composer/pcre",
"version": "3.3.2",
"source": {
"type": "git",
"url": "https://github.com/composer/pcre.git",
"reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/composer/pcre/zipball/b2bed4734f0cc156ee1fe9c0da2550420d99a21e",
"reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e",
"shasum": ""
},
"require": {
"php": "^7.4 || ^8.0"
},
"conflict": {
"phpstan/phpstan": "<1.11.10"
},
"require-dev": {
"phpstan/phpstan": "^1.12 || ^2",
"phpstan/phpstan-strict-rules": "^1 || ^2",
"phpunit/phpunit": "^8 || ^9"
},
"type": "library",
"extra": {
"phpstan": {
"includes": [
"extension.neon"
]
},
"branch-alias": {
"dev-main": "3.x-dev"
}
},
"autoload": {
"psr-4": {
"Composer\\Pcre\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Jordi Boggiano",
"email": "j.boggiano@seld.be",
"homepage": "http://seld.be"
}
],
"description": "PCRE wrapping library that offers type-safe preg_* replacements.",
"keywords": [
"PCRE",
"preg",
"regex",
"regular expression"
],
"support": {
"issues": "https://github.com/composer/pcre/issues",
"source": "https://github.com/composer/pcre/tree/3.3.2"
},
"funding": [
{
"url": "https://packagist.com",
"type": "custom"
},
{
"url": "https://github.com/composer",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/composer/composer",
"type": "tidelift"
}
],
"time": "2024-11-12T16:29:46+00:00"
},
{
"name": "fakerphp/faker",
"version": "v1.24.1",
@@ -10945,7 +11240,9 @@
"prefer-stable": true,
"prefer-lowest": false,
"platform": {
"php": "^8.4"
"php": "^8.4",
"ext-xmlreader": "*",
"ext-simplexml": "*"
},
"platform-dev": {},
"plugin-api-version": "2.9.0"

View File

@@ -0,0 +1,29 @@
<?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('categories', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('slug')->unique();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('categories');
}
};

View File

@@ -0,0 +1,29 @@
<?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('systems', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('slug')->unique();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('systems');
}
};

View File

@@ -0,0 +1,29 @@
<?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('levels', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('slug')->unique();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('levels');
}
};

View 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::table('entries', function (Blueprint $table) {
$table->foreignId('level_id')->nullable()->constrained('levels')->nullOnDelete();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('entries', function (Blueprint $table) {
$table->dropColumn('level_id');
});
}
};

View 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('entry_systems', function (Blueprint $table) {
$table->foreignId('entry_id')->constrained('entries')->cascadeOnDelete();
$table->foreignId('system_id')->constrained('systems')->cascadeOnDelete();
$table->primary(['entry_id', 'system_id']);
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('entry_systems');
}
};

View 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('entry_categories', function (Blueprint $table) {
$table->foreignId('entry_id')->constrained('entries')->cascadeOnDelete();
$table->foreignId('category_id')->constrained('categories')->cascadeOnDelete();
$table->primary(['entry_id', 'category_id']);
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('entry_categories');
}
};

View 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::table('categories', function (Blueprint $table) {
$table->json('restricted_to')->nullable();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('categories', function (Blueprint $table) {
$table->dropColumn('restricted_to');
});
}
};

View File

@@ -0,0 +1,40 @@
<?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::rename('entry_galleries', 'galleries');
Schema::table('galleries', function (Blueprint $table) {
$table->string('galleryable_type')->default('App\\Models\\Entry')
->after('id');
$table->renameColumn('entry_id', 'galleryable_id');
$table->index(['galleryable_type', 'galleryable_id']);
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('galleries', function (Blueprint $table) {
$table->dropColumn('galleryable_type');
$table->renameColumn('galleryable_id', 'entry_id');
$table->dropIndex(['galleryable_type', 'galleryable_id']);
});
Schema::rename('galleries', 'entry_galleries');
}
};

View File

@@ -0,0 +1,27 @@
<?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('news', function (Blueprint $table) {
$table->id();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('news');
}
};

View File

@@ -63,7 +63,7 @@ ul {
--error: #e57373;
--info: #1976d2;
--success: #81c784;
--success2: #388e3c;
--success2: #fdeb0f;
/* Typo */
--typography: 'Segoe UI', 'San Francisco', 'Helvetica Neue', sans-serif;

View File

@@ -104,6 +104,11 @@
color: var(--text);
border-color: var(--success2);
}
.badge.yellow, .badge.utilities {
background-color: #fdeb0f;
color: #000;
border-color: #fdeb0f;
}
.topbar-badge {
position: absolute;

View File

@@ -528,3 +528,44 @@
}
.author-search-item:hover { background-color: var(--bg3); }
.game-selector-mode {
display: flex;
gap: 0;
margin-bottom: 15px;
border: 1px solid var(--border);
}
.game-selector-mode-btn {
display: flex;
align-items: center;
gap: 7px;
padding: 8px 14px;
background: none;
border: none;
border-right: 1px solid var(--border);
color: var(--text2);
font-size: 0.85rem;
cursor: pointer;
font-family: var(--font-family);
transition: background-color 0.1s, color 0.1s;
}
.game-selector-mode-btn:last-child {
border-right: none;
}
.game-selector-mode-btn:hover {
background-color: var(--bg3);
color: var(--text);
}
.game-selector-mode-btn.active {
background-color: var(--bg3);
color: var(--rhpz-orange);
border-bottom: 2px solid var(--rhpz-orange);
}
.game-selector-platform-only {
grid-column: span 1;
}

View File

@@ -22,6 +22,7 @@ const ERROR_TABLE = {
uploadError: "One or more files failed to upload.",
notAllFilesDone: "Not all the files have finished uploading yet.",
noModifications: "Please select at least a type of hack.",
noSystems: "Please select at least a system.",
noDescription: "Please provide a description.",
noGame: "Please provide a game or create a new one and fill all the required fields.",
noLanguages: "Please select at least a language.",
@@ -117,6 +118,10 @@ window.SubmissionVerifications = {
return verifyCheckboxes( document.querySelector( '#modifications-group' ) );
},
step5_UtilitiesSystemsCheckboxes: function(){
return verifyCheckboxes( document.querySelector( '#systems-group' ) );
},
/**
* Verify if the description field has at least one character.
* @returns {boolean}
@@ -132,6 +137,10 @@ window.SubmissionVerifications = {
*/
step7_VerifyGame: function( element ){
const GAME_SELECTOR_MODE = document.querySelector('input[name="game_selection_mode"]')?.value ?? "game";
if( GAME_SELECTOR_MODE === 'platform' || GAME_SELECTOR_MODE === 'none' )
return true;
// Check if we have an already existent selected game.
const GAME_ID_INPUT = document.querySelector('input[name="game_id"]');
if( GAME_ID_INPUT ){
@@ -283,12 +292,18 @@ window.Submission = function(){
return false;
}
if( SECTION() === "romhacks" ){
if( SECTION() === "romhacks" || SECTION() === "lua-scripts" ){
console.log( "Step 5" );
if( !SubmissionVerifications.step5_RomhacksModificationsCheckboxes()){
this.errorKey = "noModifications";
return false;
}
} else if( SECTION() === "utilities" ){
console.log( "Step 5" );
if( !SubmissionVerifications.step5_UtilitiesSystemsCheckboxes()){
this.errorKey = "noSystems";
return false;
}
}
console.log( "Step 6" );
@@ -341,6 +356,7 @@ window.Submission = function(){
notAllFilesDone: 'uploadTarget',
uploadError: 'uploadTarget',
noModifications: 'modificationsGroup',
noSystems: 'systemsGroup',
noDescription: 'descriptionField',
noGame: 'gameSelector',
noLanguages: 'languagesGroup',

View File

@@ -0,0 +1,29 @@
<?php /** @var \App\Models\Category $category */ ?>
<div class="languages-selector form-group level" x-data="{
search: '',
selected: @js((array) $selected),
toggle(value){
const i = this.selected.indexOf(value);
i === -1 ? this.selected.push(value) : this.selected.splice(i,1);
},
valueSelect(value){
return this.selected.includes(value);
},
get count(){ return this.selected.length; }
}">
<div class="language-search">
<i data-lucide="search"></i>
<input type="text" x-model="search" placeholder="Search a category" autocomplete="off">
<button class="btn" type="button" x-show="search !== ''" @click="search = ''" x-cloak>
<i data-lucide="x"></i>
</button>
</div>
<div class="language-list" id="languages-group">
@foreach( $categories as $category )
<label class="language-item" x-show="'{{ strtolower($category->name) }}'.includes(search.toLowerCase())">
<input type="checkbox" name="categories[]" value="{{ $category->id }}" x-model="selected" :value="{{ $category->id }}" {{ in_array($category->id, $selected) ? 'checked' : '' }}> {{ $category->name }}
</label>
@endforeach
</div>
</div>

View File

@@ -24,13 +24,26 @@
@foreach( $entry->modifications as $modif )
<span class="badge orange">{{ $modif->name }}</span>
@endforeach
@elseif( section_must_be( 'translations', $entry->type ) )
@foreach( $entry->languages as $lang )
<span class="badge orange">{{ $lang->name }}</span>
@endforeach
@elseif( section_must_be( 'utilities', $entry->type ) )
@foreach( $entry->categories as $category )
<span class="badge orange">{{ $category->name }}</span>
@endforeach
@endif
@if( $entry->status_id )
<span class="badge">{{ $entry->status->name }}</span>
@endif
@foreach( $entry->languages as $lang )
<span class="badge">{{ $lang->name }}</span>
@endforeach
@if( $entry->level_id )
<span class="badge">{{ $entry->level->name }}</span>
@endif
@if( section_must_not_be( 'translations', $entry->type ) )
@foreach( $entry->languages as $lang )
<span class="badge">{{ $lang->name }}</span>
@endforeach
@endif
</div>
<div class="entry-card-meta">
<span><i data-lucide="download" size="12"></i> x</span>

View File

@@ -1,4 +1,4 @@
<?php /** @var \App\Models\EntryGallery $galleryItem */ ?>
<?php /** @var \App\Models\Gallery $galleryItem */ ?>
@extends('layouts.app')
@section('page-title', $entry->title . " - " . config('app.name') )
@@ -54,9 +54,13 @@
</h1>
<div class="entry-authors">
@forelse( $entry->authors as $author)
@if($loop->first)By @endif
@if($loop->first)
By
@endif
{{ $author->name }}
@if( !$loop->last ), @endif
@if( !$loop->last )
,
@endif
@empty
No authors
@endforelse
@@ -65,7 +69,7 @@
@if($entry->user_id)
<span>
<i data-lucide="user" size="14"></i>
Posted by <x-xf-username-link :user-id="$entry->user_id" />
Posted by <x-xf-username-link :user-id="$entry->user_id"/>
</span>
@endif
@@ -83,43 +87,68 @@
</div>
<div class="entry-meta-grid">
@if( $entry->game )
<x-entry-meta-item label="Game Name" value="{{ $entry->game->name }}" route="{!! databaseRoute( [ 'games' => [ $entry->game->id ], 'platforms' => [ $entry->getRealPlatform()?->id ] ] ) !!}" />
<x-entry-meta-item label="Game Name" value="{{ $entry->game->name }}"
route="{!! databaseRoute( [ 'games' => [ $entry->game->id ], 'platforms' => [ $entry->getRealPlatform()?->id ] ] ) !!}"/>
@endif
@if( $entry->getRealPlatform() )
<x-entry-meta-item label="Platform" value="{{ ($entry->getRealPlatform())->name }}" route="{!! databaseRoute( ['platforms' => [ $entry->getRealPlatform()->id ] ] ) !!}" />
<x-entry-meta-item label="Platform" value="{{ ($entry->getRealPlatform())->name }}"
route="{!! databaseRoute( ['platforms' => [ $entry->getRealPlatform()->id ] ] ) !!}"/>
@endif
@if( $entry->game && $entry->game->genre )
<x-entry-meta-item label="Genre" value="{{ $entry->game->genre->name }}" route="{!! databaseRoute( ['genres' => [ $entry->game->genre->id ] ]) !!}" />
<x-entry-meta-item label="Genre" value="{{ $entry->game->genre->name }}"
route="{!! databaseRoute( ['genres' => [ $entry->game->genre->id ] ]) !!}"/>
@endif
@if( $entry->languages->isNotEmpty() )
<x-entry-meta-item label="Language" value="{{ $entry->languages->pluck('name')->implode(', ') }}" route="{!! databaseRoute( [ 'languages' => $entry->languages->pluck('id')->toArray() ]) !!}" />
<x-entry-meta-item label="Language"
value="{{ $entry->languages->pluck('name')->implode(', ') }}"
route="{!! databaseRoute( [ 'languages' => $entry->languages->pluck('id')->toArray() ]) !!}"/>
@endif
@if( $entry->status_id )
<x-entry-meta-item label="Status" value="{{ $entry->status->name }}" route="{!! databaseRoute( ['statuses' => [ $entry->status->id ] ]) !!}" />
<x-entry-meta-item label="Status" value="{{ $entry->status->name }}"
route="{!! databaseRoute( ['statuses' => [ $entry->status->id ] ]) !!}"/>
@endif
@if( $entry->level_id )
<x-entry-meta-item label="Experience Level" value="{{ $entry->level->name }}"
route="{!! databaseRoute( ['levels' => [ $entry->level->id ] ]) !!}"/>
@endif
@if( $entry->modifications->isNotEmpty() )
<x-entry-meta-item label="Type of hack" value="{{ $entry->modifications->pluck('name')->implode(', ') }}" route="{!! databaseRoute( [ 'modifications' => $entry->modifications->pluck('id')->toArray() ] ) !!}" />
<x-entry-meta-item label="Type of hack"
value="{{ $entry->modifications->pluck('name')->implode(', ') }}"
route="{!! databaseRoute( [ 'modifications' => $entry->modifications->pluck('id')->toArray() ] ) !!}"/>
@endif
@if( $entry->categories->isNotEmpty() )
<x-entry-meta-item label="Categories"
value="{{ $entry->categories->pluck('name')->implode(', ') }}"
route="{!! databaseRoute( [ 'categories' => $entry->categories->pluck('id')->toArray() ] ) !!}"/>
@endif
@if( $entry->systems->isNotEmpty() )
<x-entry-meta-item label="OS" value="{{ $entry->systems->pluck('name')->implode(', ') }}"
route="{!! databaseRoute( [ 'systems' => $entry->systems->pluck('id')->toArray() ] ) !!}"/>
@endif
@if( $entry->version )
<x-entry-meta-item label="Version" value="{{ $entry->version }}" route="none" />
<x-entry-meta-item label="Version" value="{{ $entry->version }}" route="none"/>
@endif
@if( $entry->release_date )
<x-entry-meta-item label="Release Date" value="{{ $entry->release_date->format('Y-m-d') }}" route="none" />
<x-entry-meta-item label="Release Date" value="{{ $entry->release_date->format('Y-m-d') }}"
route="none"/>
@endif
</div>
<div class="hack-actions" style="display:flex;gap:10px;">
@if($entry->state === 'pending')
@can('approve', $entry)
<div x-data="{ rejectOpen: false }">
<form action="{{ route('queue.approve', $entry) }}" method="POST" style="display:inline">
<form action="{{ route('queue.approve', $entry) }}" method="POST"
style="display:inline">
@csrf
@method('PATCH')
<button type="submit" class="btn success" onclick="return confirm('Approve this entry?')">
<button type="submit" class="btn success"
onclick="return confirm('Approve this entry?')">
<i data-lucide="check-circle" size="14"></i>
Approve
</button>
</form>
<button type="button" class="btn danger" style="margin-right:15px;" @click="rejectOpen = !rejectOpen">
<button type="button" class="btn danger" style="margin-right:15px;"
@click="rejectOpen = !rejectOpen">
<i data-lucide="x-circle" size="14"></i>
Reject
</button>
@@ -145,7 +174,7 @@
@csrf
@method('PATCH')
<div class="form-group">
<x-form-field-title name="Rejection reason" required="true" />
<x-form-field-title name="Rejection reason" required="true"/>
<textarea
class="form-input"
name="reason"
@@ -171,11 +200,13 @@
</div>
@endcan
@endif
<button class="btn primary" onclick="Livewire.dispatch('entryOpenFilesModal', { entryId: {{ $entry->id }} })">
<button class="btn primary"
onclick="Livewire.dispatch('entryOpenFilesModal', { entryId: {{ $entry->id }} })">
<i data-lucide="download"></i> Download
</button>
@can('update',$entry)
<a href="{{ route('submit.edit', ['section' => $entry->type, 'entry' => $entry ] ) }}" class="btn">
<a href="{{ route('submit.edit', ['section' => $entry->type, 'entry' => $entry ] ) }}"
class="btn">
<i data-lucide="edit"></i> Edit
</a>
@endcan
@@ -190,13 +221,13 @@
<div class="entry-content">
@if( $entry->description )
<x-entry-section-title label="Description" icon="file-text" />
<x-entry-section-title label="Description" icon="file-text"/>
<div class="entry-description">
{{ $entry->description }}
</div>
@endif
@if( $entry->hashes->isNotEmpty() )
<x-entry-section-title label="Hashes" icon="table-properties" />
<x-entry-section-title label="Hashes" icon="table-properties"/>
<div class="entry-description">
@foreach( $entry->hashes->all() as $hash )
Filename: {{ $hash->filename }}<br>
@@ -207,7 +238,7 @@
</div>
@endif
@if( $entry->parseStaffCredits() )
<x-entry-section-title label="Staff Credits" icon="users-round" />
<x-entry-section-title label="Staff Credits" icon="users-round"/>
<div class="entry-description">
<ul>
@foreach( $entry->parseStaffCredits() as $item )
@@ -218,13 +249,16 @@
@endif
@if( $entry->gallery->isNotEmpty() )
<div x-data="{ open: false, currentImage: ''}" x-cloak>
<x-entry-section-title label="Gallery" icon="images" />
<x-entry-section-title label="Gallery" icon="images"/>
<div class="entry-gallery">
@foreach( $entry->gallery as $galleryItem )
<div class="entry-gallery-item" @click="currentImage = '{{ Storage::url($galleryItem->image) }}'; open = true; "><img src="{{ Storage::url($galleryItem->image) }}"></div>
<div class="entry-gallery-item"
@click="currentImage = '{{ Storage::url($galleryItem->image) }}'; open = true; "><img
src="{{ Storage::url($galleryItem->image) }}"></div>
@endforeach
</div>
<div class="gallery-modal" x-show="open" x-transition.opacity.duration.300ms @click="open = false" @keydown.escape.window="open = false">
<div class="gallery-modal" x-show="open" x-transition.opacity.duration.300ms @click="open = false"
@keydown.escape.window="open = false">
<span class="gallery-modal-close" @click="open = false;"><i data-lucide="x"></i></span>
<div class="gallery-modal-content" @click.stop>
<img :src="currentImage">
@@ -233,7 +267,7 @@
</div>
@endif
@if( $entry->relevant_link )
<x-entry-section-title label="Relevant Link" icon="link" />
<x-entry-section-title label="Relevant Link" icon="link"/>
<div class="entry-description">
<a href="{{ $entry->relevant_link }}" target="_blank">{{ $entry->relevant_link }}</a>
</div>
@@ -241,19 +275,24 @@
@if( $entry->getYoutubeVideoId() )
<div x-data="{open: false, src: ''}" x-cloak class="youtube-section">
<x-entry-section-title label="Youtube Video" icon="play" />
<x-entry-section-title label="Youtube Video" icon="play"/>
<div class="video-thumbnail-wrapper" @click="src = 'https://www.youtube.com/embed/{{ $entry->getYoutubeVideoId() }}?autoplay=1'; open = true">
<div class="video-thumbnail-wrapper"
@click="src = 'https://www.youtube.com/embed/{{ $entry->getYoutubeVideoId() }}?autoplay=1'; open = true">
<img src="https://img.youtube.com/vi/{{ $entry->youtube_id }}/maxresdefault.jpg">
<div class="play-trigger">
<i data-lucide="play"></i>
</div>
</div>
<div class="gallery-modal" x-show="open" x-transition.opacity.duration.300ms @click="open = false; src = ''" @keydown.escape.window="open = false; src = ''">
<span class="gallery-modal-close" @click="open = false; src = '';"><i data-lucide="x"></i></span>
<div class="gallery-modal" x-show="open" x-transition.opacity.duration.300ms
@click="open = false; src = ''" @keydown.escape.window="open = false; src = ''">
<span class="gallery-modal-close" @click="open = false; src = '';"><i
data-lucide="x"></i></span>
<div class="gallery-modal-video" @click.stop>
<iframe :src="src" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
<iframe :src="src"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowfullscreen></iframe>
</div>
</div>
</div>

View File

@@ -52,6 +52,15 @@
{{-- Modifications --}}
<x-database-filter-with-mode title="Modifications" :items="$allModifications" model="modifications" mode-model="modificationsMode" :selected-mode="$modificationsMode" />
{{-- Categories --}}
<x-database-filter-with-mode title="Categories" :items="$allCategories" model="categories" mode-model="categoriesMode" :selected-mode="$categoriesMode" />
{{-- Levels --}}
<x-database-filter-without-mode title="Level" :items="$allLevels" model="levels"/>
{{-- Systems --}}
<x-database-filter-with-mode title="Systems" :items="$allSystems" model="systems" mode-model="systemsMode" :selected-mode="$systemsMode" />
</aside>
<div class="database-results">

View File

@@ -3,59 +3,113 @@
platformId: @json(old('new-game-platform') ? (string) old('new-game-platform') : null),
genreId: @json(old('new-game-genre') ? (string) old('new-game-genre') : null)
})">
<input type="hidden" name="game_selection_mode" value="{{ $selectionMode ?? 'game' }}">
{{--
Prefill if server-side error.
--}}
@if( $canChangeSelection )
<div class="game-selector-mode">
<button
type="button"
class="game-selector-mode-btn {{ $selectionMode === 'game' ? 'active' : '' }}"
wire:click="setSelectionMode('game')"
>
Link to a game
</button>
<button
type="button"
class="game-selector-mode-btn {{ $selectionMode === 'platform' ? 'active' : '' }}"
wire:click="setSelectionMode('platform')"
>
Platform only
</button>
<button
type="button"
class="game-selector-mode-btn {{ $selectionMode === 'none' ? 'active' : '' }}"
wire:click="setSelectionMode('none')"
>
Nothing
</button>
</div>
@endif
<div class="form-group grid-c3">
@if( !$newGame && !$hasOldNewGame )
{{-- Search game mode --}}
<div class="game-selector">
<div class="game-selector-level2">
@if($selectionMode === 'none')
<div style="color: var(--text2); font-size: 0.88rem; padding: 10px 0;">
<i data-lucide="info" size="13"></i>
This entry will not be linked to any game or platform.
</div>
@elseif( $selectionMode === 'platform' )
<input type="hidden" name="platform_only_id" value="{{ $platformModeId ?? '' }}">
<x-form-field-title name="Game" required="true" />
<input class="form-input" type="text" wire:model.live.debounce="search" placeholder="Search a game..." autocomplete="off"
@focus="$wire.dropdown = $wire.search.length >= {{ $required_chars }}" @click.outside="$wire.dropdown = false" >
<div class="game-selector-platform-only">
<x-form-field-title name="Platform" required="true" />
<select
class="form-select"
wire:change="selectPlatformOnly($event.target.value)"
>
<option value="" disabled {{ !$platformModeId ? 'selected' : '' }}>
Select a platform...
</option>
@foreach($platforms as $platform)
<option
value="{{ $platform->id }}"
{{ $platformModeId == $platform->id ? 'selected' : '' }}
>
{{ $platform->name }}
</option>
@endforeach
</select>
</div>
@else
@if( !$newGame && !$hasOldNewGame )
{{-- Search game mode --}}
<div class="game-selector">
<div class="game-selector-level2">
<input type="hidden" name="game_id" value="{{ $gameId ?? '' }}" />
<x-form-field-title name="Game" required="true" />
<input class="form-input" type="text" wire:model.live.debounce="search" placeholder="Search a game..." autocomplete="off"
@focus="$wire.dropdown = $wire.search.length >= {{ $required_chars }}" @click.outside="$wire.dropdown = false" >
<input type="hidden" name="game_id" value="{{ $gameId ?? '' }}" />
</div>
@if( $dropdown )
{{-- List games --}}
<ul class="game-selector-dropdown">
@forelse($games as $game)
<li>
<button type="button" wire:click="selectGame({{ $game->id }}, '{{ addslashes($game->name) }}')"
class="dropdown-item" {{ $gameId === $game->id ? 'selected' : '' }} >
<span class="dropdown-item-name">{{ $game->name }}</span>
@if($game->platform)
<span class="badge">{{ $game->platform->short_name }}</span>
@endif
@if($game->genre)
<span class="badge">{{ $game->genre->name }}</span>
@endif
</button>
</li>
@empty
<li class="dropdown-empty">No games found</li>
@endforelse
</ul>
@endif
</div>
@if( $dropdown )
{{-- List games --}}
<ul class="game-selector-dropdown">
@forelse($games as $game)
<li>
<button type="button" wire:click="selectGame({{ $game->id }}, '{{ addslashes($game->name) }}')"
class="dropdown-item" {{ $gameId === $game->id ? 'selected' : '' }} >
<span class="dropdown-item-name">{{ $game->name }}</span>
@if($game->platform)
<span class="badge">{{ $game->platform->short_name }}</span>
@endif
@if($game->genre)
<span class="badge">{{ $game->genre->name }}</span>
@endif
</button>
</li>
@empty
<li class="dropdown-empty">No games found</li>
@endforelse
</ul>
@endif
<div class="platform-prefilled">
<x-form-field-title name="Platform" helper="Prefilled" />
<input class="form-input" disabled="disabled" type="text" autocomplete="off" value="{{ $platformName }}">
</div>
</div>
<div class="genre-prefilled">
<x-form-field-title name="Genre" helper="Prefilled" />
<input class="form-input" disabled="disabled" type="text" autocomplete="off" value="{{ $genreName }}" >
</div>
<div class="platform-prefilled">
<x-form-field-title name="Platform" helper="Prefilled" />
<input class="form-input" disabled="disabled" type="text" autocomplete="off" value="{{ $platformName }}">
</div>
<div class="genre-prefilled">
<x-form-field-title name="Genre" helper="Prefilled" />
<input class="form-input" disabled="disabled" type="text" autocomplete="off" value="{{ $genreName }}" >
</div>
@else {{-- New game --}}
@else {{-- New game --}}
<div class="new-game-title">
<x-form-field-title name="Game" required="true" />
@@ -65,12 +119,12 @@
<div class="new-game-platform">
<x-form-field-title name="Platform" required="true" />
<select class="form-select" name="new-game-platform" x-model="platformId" required>
<option value="" disabled>---</option>
@foreach( $platforms as $platform )
<option value="{{ (string) $platform->id }}">{{ $platform->name }}</option>
@endforeach
</select>
</div>
<option value="" disabled>---</option>
@foreach( $platforms as $platform )
<option value="{{ (string) $platform->id }}">{{ $platform->name }}</option>
@endforeach
</select>
</div>
<div class="new-game-genre">
<x-form-field-title name="Genre" required="true" />
@@ -82,17 +136,19 @@
</select>
</div>
@endif
@endif
</div>
<div style="display:flex;align-items: flex-end;justify-content: right;gap:15px;">
@if($gameId)
<button type="button" class="btn" wire:click="clearGame">
Remove
</button>
@endif
<button type="button" class="btn primary" wire:click="switchNewGame">{{ $newGame ? "Cancel" : "Add a game" }}</button>
</div>
@if( $selectionMode === 'game')
<div style="display:flex;align-items: flex-end;justify-content: right;gap:15px;">
@if($gameId)
<button type="button" class="btn" wire:click="clearGame">
Remove
</button>
@endif
<button type="button" class="btn primary" wire:click="switchNewGame">{{ $newGame ? "Cancel" : "Add a game" }}</button>
</div>
@endif
</div>

View File

@@ -47,7 +47,7 @@
</div>
@endif
@if( section_must_be( 'romhacks', $section ) )
@if( section_must_be( ['romhacks', 'lua-scripts'], $section ) )
<div class="form-group">
<x-form-field-title name="{{ $words['type_of_hack'] }}" required="true" />
<div class="form-type-of-checkboxes form-group level" id="modifications-group" x-ref="modificationsGroup">
@@ -57,9 +57,26 @@
</div>
<div class="form-error-text" x-show="errorKey === 'noModifications'" x-text="errorMessage"></div>
</div>
@elseif( section_must_be( [ 'utilities', 'documents' ], $section ) )
<div class="form-group">
<x-form-field-title name="Categories" required="true" />
<x-category-selector :section="$section" :selected="$oldCategories" />
</div>
@endif
@if( section_must_be( ['romhacks', 'translations', 'homebrew'], $section ) )
@if( section_must_be( 'utilities', $section ) )
<div class="form-group">
<x-form-field-title name="{{ $words['system'] }}" required="true" />
<div class="form-type-of-checkboxes form-group level" id="systems-group" x-ref="systemsGroup">
@foreach( $systems as $system )
<label><input class="form-checkbox" type="checkbox" name="systems[]" value="{{ $system->id }}" {{ in_array($system->id, $oldSystems) ? 'checked' : '' }}>{{ $system->name }}</label>
@endforeach
</div>
<div class="form-error-text" x-show="errorKey === 'noSystems'" x-text="errorMessage"></div>
</div>
@endif
@if( section_must_be( ['romhacks', 'translations', 'homebrew', 'utilities', 'documents', 'lua-scripts'], $section ) )
<div class="form-group grid-c3">
<div>
<x-form-field-title name="{{ $words['version'] }}" required="true" />
@@ -71,17 +88,26 @@
<input type="date" class="form-input" name="release-date" value="{{ old('release-date') ?? $entry->release_date?->format('Y-m-d') ?? '' }}" required>
</div>
<div>
<x-form-field-title name="{{ $words['status'] }}" required="true" />
@if( section_must_be( ['utilities', 'documents'], $section ) )
<x-form-field-title name="{{ $words['level'] }}" required="true" />
<div class="form-status-radio form-group level">
@foreach( $statuses as $status )
<label><input class="form-radio" type="radio" name="status" value="{{ $status->id }}" {{ old('status', $entry->status_id ) == $status->id ? 'checked' : '' }} required>{{ $status->name }}</label>
@endforeach
</div>
@foreach( $levels as $level )
<label><input class="form-radio" type="radio" name="level" value="{{ $level->id }}" {{ old('level', $entry->level_id ) == $level->id ? 'checked' : '' }} required>{{ $level->name }}</label>
@endforeach
</div>
@else
<x-form-field-title name="{{ $words['status'] }}" required="true" />
<div class="form-status-radio form-group level">
@foreach( $statuses as $status )
<label><input class="form-radio" type="radio" name="status" value="{{ $status->id }}" {{ old('status', $entry->status_id ) == $status->id ? 'checked' : '' }} required>{{ $status->name }}</label>
@endforeach
</div>
@endif
</div>
</div>
@endif
@if( section_must_be( 'translations', $section ) )
@if( section_must_be( [ 'translations', 'utilities', 'documents'] , $section ) )
<x-form-field-title name="Languages" required="true" />
<x-languages-selector :selected="$oldLanguages" />
@error('languages')
@@ -104,10 +130,12 @@
<x-form-group-title label="{{ $words['about_game'] }}" icon="gamepad-2" />
<div x-ref="gameSelector">
<livewire:game-selector
:game-id="old('game_id', $entry->game_id ?? null )"
:game-id="old('game_id', $entry->game_id ?? null)"
:new-game-title="old('new-game-title')"
:new-game-platform="old('new-game-platform')"
:new-game-genre="old('new-game-genre')"
:platform-only-id="old('platform_only_id', $entry->platform_id ?? null)"
:section="$section"
/>
</div>
<div class="form-error-text" x-show="errorKey === 'noGame'" x-text="errorMessage"></div>
@@ -123,9 +151,11 @@
@error('new-game-genre')
<x-form-error-text message="{{ $message }}" />
@enderror
<livewire:hashes-upload :old-hashes="old('hashes', $entry->hashes->toArray(), [])" />
@if( section_must_be( [ 'romhacks', 'translations' ], $section ))
<livewire:hashes-upload :old-hashes="old('hashes', $entry->hashes->toArray(), [])" />
@endif
@if( section_must_not_be( 'translations', $section ) )
@if( section_must_not_be( [ 'translations', 'utilities', 'documents' ], $section ) )
<x-form-field-title name="Languages" required="true" />
<x-languages-selector :selected="$oldLanguages" />
@error('languages')
@@ -156,17 +186,21 @@
@enderror
<x-staff-credits-field :old-staff-credits="old('staff_credits', $entry->staff_credits ?? null)" />
<x-form-group-title label="{{ $words['related_links'] }}" icon="link" />
<div class="form-group grid-c2">
<div>
<x-form-field-title name="{{ $words['release_site'] }}" helper="{{ $words['release_site_helper'] }}" required="" />
<input class="form-input" type="url" name="release_site" value="{{ old( 'release_site', $entry->relevant_link ?? '' ) }}">
@if( section_must_not_be( 'documents', $section ) )
<x-form-group-title label="{{ $words['related_links'] }}" icon="link" />
<div class="form-group grid-c2">
<div>
<x-form-field-title name="{{ $words['release_site'] }}" helper="{{ $words['release_site_helper'] }}" required="" />
<input class="form-input" type="url" name="release_site" value="{{ old( 'release_site', $entry->relevant_link ?? '' ) }}">
</div>
<div>
<x-form-field-title name="{{ $words['youtube_video'] }}" required="" />
<input class="form-input" type="url" name="youtube_video" value="{{ old( 'youtube_video', $entry->youtube_link ?? '' ) }}">
</div>
</div>
<div>
<x-form-field-title name="{{ $words['youtube_video'] }}" required="" />
<input class="form-input" type="url" name="youtube_video" value="{{ old( 'youtube_video', $entry->youtube_link ?? '' ) }}">
</div>
</div>
@else
<div class="form-group"></div>
@endif
@if($isEdit)
<x-form-group-title label="Entry Management" icon="wrench" />

View File

@@ -14,11 +14,11 @@ Route::name('entries.')->controller(EntryController::class)->group(function () {
Route::get('/database', 'index' )->name('index');
Route::get('/{section}', 'section_redirect' )->name('section_redirect')
->where(['section' => 'translations|romhacks|homebrew|utilities|documents|lua-scripts|tutorials']);
->where(['section' => 'translations|romhacks|homebrew|utilities|documents|lua-scripts']);
Route::get('/{section}/{entry:slug}', 'show' )->name('show')->where(
[
'section' => 'translations|romhacks|homebrew|utilities|documents|lua-scripts|tutorials',
'section' => 'translations|romhacks|homebrew|utilities|documents|lua-scripts',
'entry' => '[a-zA-Z0-9\-_]+'
]
);
@@ -30,18 +30,18 @@ Route::name('entries.')->controller(EntryController::class)->group(function () {
// SubmissionController.
Route::name('submit.')->prefix('/submit')->controller(\App\Http\Controllers\SubmissionController::class)->middleware(['xf.auth', 'can:create,\App\Models\Entry'])->group(function () {
Route::get('/{section}', 'create' )->name('create')
->where([ 'section' => 'translations|romhacks|homebrew|utilities|documents|lua-scripts|tutorials' ]);
->where([ 'section' => 'translations|romhacks|homebrew|utilities|documents|lua-scripts' ]);
Route::post('/{section}', 'store' )->name('store')
->where([ 'section' => 'translations|romhacks|homebrew|utilities|documents|lua-scripts|tutorials' ]);
->where([ 'section' => 'translations|romhacks|homebrew|utilities|documents|lua-scripts' ]);
});
Route::name('submit.')->prefix('/edit')->controller(\App\Http\Controllers\SubmissionController::class)->middleware(['xf.auth', 'can:update,entry'])->group(function () {
Route::get('/{section}/{entry:id}', 'edit' )->name('edit')
->where([ 'section' => 'translations|romhacks|homebrew|utilities|documents|lua-scripts|tutorials', 'entry' => '[0-9\-]+' ]);
->where([ 'section' => 'translations|romhacks|homebrew|utilities|documents|lua-scripts', 'entry' => '[0-9\-]+' ]);
Route::post('/{section}/{entry:id}', 'update' )->name('update')
->where([ 'section' => 'translations|romhacks|homebrew|utilities|documents|lua-scripts|tutorials', 'entry' => '[0-9\-]+' ]);
->where([ 'section' => 'translations|romhacks|homebrew|utilities|documents|lua-scripts', 'entry' => '[0-9\-]+' ]);
Route::delete('/{section}/{entry:id}', 'destroy' )->name('destroy')
->where([ 'section' => 'translations|romhacks|homebrew|utilities|documents|lua-scripts|tutorials', 'entry' => '[0-9\-]+' ]);
->where([ 'section' => 'translations|romhacks|homebrew|utilities|documents|lua-scripts', 'entry' => '[0-9\-]+' ]);
});
// QueueController