Initial commit

This commit is contained in:
2026-05-20 18:25:15 +02:00
commit 95f0b4ff01
288 changed files with 90909 additions and 0 deletions

View File

@@ -0,0 +1,149 @@
<?php
namespace App\Livewire;
use App\Models\Author;
use App\Models\Game;
use App\Models\Genre;
use App\Models\Platform;
use Illuminate\View\View;
use Livewire\Component;
class AuthorsSelector extends Component
{
public string $search = '';
public bool $newAuthor = false;
public string $newAuthorName = '';
public array $selectedAuthors = []; // 'id', 'name'
public array $newAuthors = [];
public bool $dropdown = false;
public function mount( array $oldAuthors = [], array $oldNewAuthors = [] ): void
{
$selectedAuthors = [];
if ( is_string($oldAuthors) || is_int($oldAuthors) ) {
$authors = [ $oldAuthors ];
}
if ( is_array($oldAuthors) && $oldAuthors !== [] ) {
$firstItem = reset($oldAuthors);
if ( is_array($firstItem) && array_key_exists('id', $firstItem) ) {
$selectedAuthors = $oldAuthors;
} else {
$authorRecords = Author::whereIn('id', array_filter($oldAuthors, fn($id) => $id !== null))->get()->keyBy('id');
foreach ($oldAuthors as $authorId) {
if ( isset($authorRecords[$authorId]) ) {
$selectedAuthors[] = [
'id' => $authorRecords[$authorId]->id,
'name' => $authorRecords[$authorId]->name,
];
}
}
}
}
if ( is_string($oldNewAuthors) || is_int($oldNewAuthors) ) {
$newAuthors = [ $oldNewAuthors ];
}
foreach ( (array) $oldNewAuthors as $name ) {
if ( trim((string) $name) === '' ) {
continue;
}
$selectedAuthors[] = [
'id' => null,
'name' => trim((string) $name),
];
}
$this->selectedAuthors = $selectedAuthors;
$this->newAuthors = $oldNewAuthors;
}
public function updatedSearch(): void
{
$this->dropdown = strlen($this->search) > 2;
}
public function selectAuthor( int $id, string $name ): void
{
foreach ( $this->selectedAuthors as $author ) {
if ( $author['id'] === $id ) {
$this->search = '';
$this->dropdown = false;
return;
}
}
$this->selectedAuthors[] = [
'id' => $id,
'name' => $name,
];
$this->search = '';
$this->dropdown = false;
}
public function removeAuthor( int $i ): void
{
array_splice( $this->selectedAuthors, $i, 1 );
}
public function addNewAuthor(): void
{
if( empty( trim( $this->newAuthorName ) ) )
return;
foreach ( $this->selectedAuthors as $author ) {
if( strtolower($author['name']) === strtolower( $this->newAuthorName ) ){
$this->newAuthor = false;
$this->newAuthorName = '';
return;
}
}
$this->selectedAuthors[] = [
'id' => null,
'name' => trim( $this->newAuthorName ),
];
$this->newAuthor = false;
$this->newAuthorName = '';
}
public function switchNewAuthor(): void
{
$this->newAuthor = !$this->newAuthor;
$this->newAuthorName = '';
$this->search = '';
$this->dropdown = false;
}
public function render(): View
{
$authors = collect();
$data = [];
if( $this->dropdown && strlen( $this->search ) > 2 && !$this->newAuthor ){
$ids = array_filter(array_column( $this->selectedAuthors, 'id' ), function ( $id ) {
return !is_null( $id );
});
$authors = Author::where( 'name', 'like', '%' . $this->search . '%' )
->when( !empty( $ids ), function ( $query ) use ( $ids ) { $query->whereNotIn( 'id', $ids ); } )
->orderBy( 'name' )
->limit( 20 )
->get();
$data['authors'] = $authors;
}
return view('livewire.authors-selector', $data );
}
}

View File

@@ -0,0 +1,184 @@
<?php
namespace App\Livewire;
use App\Models\Game;
use App\Models\Genre;
use App\Models\Platform;
use Illuminate\View\View;
use Livewire\Component;
/**
* Game Selector for existing games and new games.
*/
class GameSelector extends Component
{
public const int REQUIRED_CHARS = 3;
/**
* If we are in new game mode.
* @var bool
*/
public bool $newGame = false;
/**
* Current user game search.
* @var string
*/
public string $search = '';
/**
* Existing game ID selected.
* @var int|null
*/
public ?int $gameId = null;
/**
* Existing game name selected or new game name.
* @var string|null
*/
public ?string $gameName = null;
/**
* New game platform ID.
* @var int|null
*/
public ?int $gamePlatformId = null;
/**
* New game genre ID.
* @var int|null
*/
public ?int $gameGenreId = null;
/**
* Existing game platform name or new game platform name.
* @var string|null
*/
public ?string $platformName = null;
/**
* Existing game genre name or new game genre name.
* @var string|null
*/
public ?string $genreName = null;
/**
* If dropdown must be rendered or not.
* @var bool
*/
public bool $dropdown = false;
public function mount( ?int $gameId = null, ?string $newGameTitle = null, ?int $newGamePlatform = null, ?int $newGameGenre = null ): void
{
// If we selected an existent game.
if( $gameId ){
$game = Game::with(['platform','genre'])->find($gameId);
if( $game ){
$this->gameId = $game->id;
$this->gameName = $game->name;
$this->platformName = $game->platform->name;
$this->genreName = $game->genre->name;
$this->search = $game->name;
$this->newGame = false;
return;
}
}
if( $newGameTitle || $newGamePlatform || $newGameGenre ){
$this->newGame = true;
$this->gameName = $newGameTitle;
$this->gamePlatformId = is_numeric($newGamePlatform) ? (int) $newGamePlatform : null;
$this->gameGenreId = is_numeric($newGameGenre) ? (int) $newGameGenre : null;
}
}
/**
* If we update search bar.
* @return void
*/
public function updatedSearch(): void
{
if( $this->gameId ){
$this->gameId = null;
$this->gameName = null;
$this->platformName = null;
$this->genreName = null;
}
$this->dropdown = strlen($this->search) >= self::REQUIRED_CHARS;
}
/**
* Select an existent game.
*
* @param int $id
* @param string $name
*
* @return void
*/
public function selectGame( int $id, string $name ): void
{
$game = Game::with(['platform','genre'])->find($id);
if( $game ){
$this->gameId = $game->id;
$this->gameName = $game->name;
$this->platformName = $game->platform->name;
$this->genreName = $game->genre->name;
$this->search = $game->name;
$this->dropdown = false;
$this->dispatch( 'game-selected', id: $id ); // Send an event to the JS part.
}
}
/**
* Clear existent game selection.
* @return void
*/
public function clearGame(): void
{
$this->gameId = null;
$this->gameName = null;
$this->platformName = null;
$this->genreName = null;
$this->search = '';
$this->dispatch( 'game-selected', id: null ); // Send an event to the JS part.
}
/**
* Switch mode.
* @return void
*/
public function switchNewGame(): void
{
$this->clearGame();
$this->newGame = !$this->newGame;
}
public function render(): View
{
$games = collect();
// Need to search games for dropdown.
if( $this->dropdown && strlen($this->search) >= self::REQUIRED_CHARS && $this->newGame === false ){
$games = Game::with(['platform','genre'])
->where('name', 'like', '%'.$this->search.'%')
->orderBy('name')
->limit(20)
->get();
}
$data = [ 'games' => $games, 'required_chars' => self::REQUIRED_CHARS ];
if( $this->newGame === true ){ // If we want a new game, get platforms and genres.
$data['platforms'] = Platform::orderBy('name')->get();
$data['genres'] = Genre::orderBy('name')->get();
}
$data['hasOldNewGame'] = old('new-game-title') || old('new-game-platform') || old('new-game-genre');
return view('livewire.game-selector', $data );
}
}

View File

@@ -0,0 +1,77 @@
<?php
namespace App\Livewire;
use App\Models\EntryHash;
use App\Models\Game;
use App\Models\Genre;
use App\Models\Platform;
use Illuminate\View\View;
use Livewire\Component;
/**
* Hashes uploader in submission form.
* Upload hash and create fields.
*
* @phpstan-import-type HashObject from \App\Types\SubmissionTypes
*/
class HashesUpload extends Component
{
/**
* List of hashes.
* @var list<HashObject> $hashes
*/
public array $hashes = [];
/**
* Prepare old hashes.
*
* @param list<HashObject> $oldHashes
* @return void
*/
public function mount( array $oldHashes = [] ): void
{
foreach ($oldHashes as $hash) {
$this->addHash( $hash['filename'], $hash['hash_crc32'], $hash['hash_sha1'], $hash['verified'] );
}
}
/**
* Add an hash to the list.
*
* @param string $filename
* @param string $crc32
* @param string $sha1
* @param string|null $verified
*
* @return void
*/
public function addHash(string $filename, string $crc32, string $sha1, ?string $verified = null): void
{
$this->hashes[] = [
'filename' => $filename,
'hash_crc32' => $crc32,
'hash_sha1' => $sha1,
'verified' => $verified ?? "No-Intro" // TODO: Change it.
];
}
/**
* Remove a specific hash.
*
* @param int $index
* @return void
*/
public function removeHash(int $index): void
{
array_splice($this->hashes, $index, 1);
}
public function render(): View
{
return view('livewire.hashes-upload');
}
}