Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .claude/missing-translations.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
In file `translations-missing.json` there is JSON object with keys as missing translations and then nested `filled` / `missing`.

In the `missing` there is list of locales where the translations are missing and in the `filled` there is another key val object, where key is locale and value is the translation that exists.

Go through the missing translations and add them to all missing translations.

You can use parallel agents for each locale respectively.

After you are done, run checks to make sure the structure of yaml translations is correct.
6 changes: 6 additions & 0 deletions assets/controllers/puzzle_add_form_controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export default class extends Controller {
'timeAndDateSection', // Card with time + date (Speed + Relax)
'timeSection', // Time field only (Speed only)
'firstAttemptSection', // First attempt checkbox (Speed only)
'unboxedSection', // Unboxed checkbox (Speed only)
'groupSection', // Group players (Speed + Relax)
'competitionSection', // Competition (Speed only)
'commonSection', // Comment, photo (Speed + Relax)
Expand Down Expand Up @@ -69,6 +70,11 @@ export default class extends Controller {
this.firstAttemptSectionTarget.classList.toggle('d-none', !isSpeed);
}

// Unboxed checkbox (Speed only)
if (this.hasUnboxedSectionTarget) {
this.unboxedSectionTarget.classList.toggle('d-none', !isSpeed);
}

// Group section (Speed + Relax)
if (this.hasGroupSectionTarget) {
this.groupSectionTarget.classList.toggle('d-none', isCollection);
Expand Down
1 change: 0 additions & 1 deletion config/services.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
use SpeedPuzzling\Web\Doctrine\RegexSchemaAssetFilter;
use SpeedPuzzling\Web\Services\Doctrine\FixDoctrineMigrationTableSchema;
use SpeedPuzzling\Web\Services\SentryTracesSampler;
use SpeedPuzzling\Web\Services\SentryTransactionNameEnhancer;
use SpeedPuzzling\Web\Services\StripeWebhookHandler;
use Stripe\StripeClient;
use Symfony\Component\HttpClient\Psr18Client;
Expand Down
26 changes: 26 additions & 0 deletions migrations/Version20260118100000.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

declare(strict_types=1);

namespace SpeedPuzzling\Web\Migrations;

use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;

final class Version20260118100000 extends AbstractMigration
{
public function getDescription(): string
{
return 'Add unboxed column to puzzle_solving_time table';
}

public function up(Schema $schema): void
{
$this->addSql('ALTER TABLE puzzle_solving_time ADD unboxed BOOLEAN DEFAULT false NOT NULL');
}

public function down(Schema $schema): void
{
$this->addSql('ALTER TABLE puzzle_solving_time DROP unboxed');
}
}
14 changes: 14 additions & 0 deletions src/Component/PlayerSolvedPuzzles.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ final class PlayerSolvedPuzzles
#[LiveProp(writable: true)]
public bool $onlyFirstTries = false;

#[LiveProp(writable: true)]
public bool $onlyUnboxed = false;

#[LiveProp(writable: true)]
public string $sortBy = 'fastest';

Expand Down Expand Up @@ -123,6 +126,7 @@ public function resetFilters(): void
$this->searchQuery = null;
$this->onlyRelax = false;
$this->onlyFirstTries = false;
$this->onlyUnboxed = false;
$this->dateFrom = null;
$this->dateTo = null;
$this->speedValue = null;
Expand All @@ -142,6 +146,7 @@ public function populate(): void

if ($this->category !== 'solo') {
$this->onlyFirstTries = false;
$this->onlyUnboxed = false;
}

$this->ranking = $this->getRanking->allForPlayer($this->playerId);
Expand All @@ -164,6 +169,11 @@ public function populate(): void
$soloSolvedPuzzlesGrouped = $this->puzzlesSorter->filterOutNonFirstTriesGrouped($soloSolvedPuzzlesGrouped);
}

// Only apply unboxed filter if user has membership (members exclusive filter)
if ($this->onlyUnboxed === true && $this->hasMembership()) {
$soloSolvedPuzzlesGrouped = $this->puzzlesSorter->filterOutNonUnboxedGrouped($soloSolvedPuzzlesGrouped);
}

// Apply sorting
$soloSolvedPuzzlesGrouped = $this->applySortingGrouped($soloSolvedPuzzlesGrouped);
$duoSolvedPuzzles = $this->applySorting($duoSolvedPuzzles);
Expand Down Expand Up @@ -433,6 +443,10 @@ public function getActiveFiltersCount(): int
$count++;
}

if ($this->onlyUnboxed !== false) {
$count++;
}

if ($this->onlyRelax !== false) {
$count++;
}
Expand Down
18 changes: 17 additions & 1 deletion src/Component/PuzzleTimes.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ final class PuzzleTimes
#[LiveProp(writable: true)]
public bool $onlyFirstTries = false;

#[LiveProp(writable: true)]
public bool $onlyUnboxed = false;

#[LiveProp(writable: true)]
public bool $onlyFavoritePlayers = false;

Expand Down Expand Up @@ -88,19 +91,28 @@ public function populate(): void

if ($this->category !== 'solo') {
$this->onlyFirstTries = false;
$this->onlyUnboxed = false;
}

$soloPuzzleSolvers = $this->getPuzzleSolvers->soloByPuzzleId($this->puzzleId);

if ($this->onlyFirstTries === true) {
$soloPuzzleSolvers = $this->puzzlesSorter->sortByFirstTry($soloPuzzleSolvers);
$soloPuzzleSolversGrouped = $this->puzzlesSorter->groupPlayers($soloPuzzleSolvers);
$soloPuzzleSolversGrouped = $this->puzzlesSorter->filterOutNonFirstTriesGrouped($soloPuzzleSolversGrouped);
} else {
$soloPuzzleSolvers = $this->puzzlesSorter->sortByFastest($soloPuzzleSolvers);
$soloPuzzleSolversGrouped = $this->puzzlesSorter->groupPlayers($soloPuzzleSolvers);
}

// Apply filters: when both are checked, use combined filter (AND logic)
if ($this->onlyFirstTries === true && $this->onlyUnboxed === true) {
$soloPuzzleSolversGrouped = $this->puzzlesSorter->filterByFirstAttemptAndUnboxedGrouped($soloPuzzleSolversGrouped);
} elseif ($this->onlyFirstTries === true) {
$soloPuzzleSolversGrouped = $this->puzzlesSorter->filterOutNonFirstTriesGrouped($soloPuzzleSolversGrouped);
} elseif ($this->onlyUnboxed === true) {
$soloPuzzleSolversGrouped = $this->puzzlesSorter->filterOutNonUnboxedGrouped($soloPuzzleSolversGrouped);
}

$duoPuzzleSolvers = $this->getPuzzleSolvers->duoByPuzzleId($this->puzzleId);
$duoPuzzleSolvers = $this->puzzlesSorter->sortByFastest($duoPuzzleSolvers);
$duoPuzzleSolversGrouped = $this->puzzlesSorter->groupPlayers($duoPuzzleSolvers);
Expand Down Expand Up @@ -255,6 +267,10 @@ public function getActiveFiltersCount(): int
$count++;
}

if ($this->onlyUnboxed !== false) {
$count++;
}

if ($this->onlyFavoritePlayers !== false) {
$count++;
}
Expand Down
1 change: 1 addition & 0 deletions src/Controller/PuzzleAddController.php
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,7 @@ private function handleSpeedPuzzling(
groupPlayers: $groupPlayers,
finishedAt: $data->finishedAt,
firstAttempt: $data->firstAttempt,
unboxed: $data->unboxed,
),
);

Expand Down
4 changes: 4 additions & 0 deletions src/Entity/PuzzleSolvingTime.php
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ public function __construct(
public null|string $finishedPuzzlePhoto,
#[Column(options: ['default' => 0])]
public bool $firstAttempt,
#[Column(options: ['default' => 0])]
public bool $unboxed,
#[ManyToOne]
public null|CompetitionRound $competitionRound = null,
#[ManyToOne]
Expand All @@ -91,6 +93,7 @@ public function modify(
DateTimeImmutable $finishedAt,
null|string $finishedPuzzlePhoto,
bool $firstAttempt,
bool $unboxed,
null|Competition $competition,
): void {
$this->secondsToSolve = $seconds;
Expand All @@ -99,6 +102,7 @@ public function modify(
$this->finishedAt = $finishedAt;
$this->finishedPuzzlePhoto = $finishedPuzzlePhoto;
$this->firstAttempt = $firstAttempt;
$this->unboxed = $unboxed;
$this->competition = $competition;

$this->puzzlersCount = $this->calculatePuzzlersCount();
Expand Down
2 changes: 2 additions & 0 deletions src/FormData/EditPuzzleSolvingTimeFormData.php
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,8 @@ public function setTimeFromSeconds(int $totalSeconds): void

public bool $firstAttempt = false;

public bool $unboxed = false;

public function __construct()
{
$this->finishedAt = new DateTimeImmutable();
Expand Down
2 changes: 2 additions & 0 deletions src/FormData/PuzzleAddFormData.php
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ public function hasTime(): bool

public bool $firstAttempt = false;

public bool $unboxed = false;

// Speed Puzzling & Relax common fields
public null|DateTimeImmutable $finishedAt;

Expand Down
2 changes: 2 additions & 0 deletions src/FormData/PuzzleSolvingTimeFormData.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ final class PuzzleSolvingTimeFormData

public bool $firstAttempt = false;

public bool $unboxed = false;

public function __construct()
{
$this->finishedAt = new DateTimeImmutable();
Expand Down
6 changes: 6 additions & 0 deletions src/FormType/PuzzleAddFormType.php
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,12 @@ public function buildForm(FormBuilderInterface $builder, array $options): void
'help' => 'forms.first_attempt_help',
]);

$builder->add('unboxed', CheckboxType::class, [
'label' => 'forms.unboxed',
'required' => false,
'help' => 'forms.unboxed_help',
]);

// Common fields (Speed & Relax)
$builder->add('finishedAt', DateType::class, [
'label' => 'forms.date_finished',
Expand Down
6 changes: 6 additions & 0 deletions src/FormType/PuzzleSolvingTimeFormType.php
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,12 @@ public function buildForm(FormBuilderInterface $builder, array $options): void
'help' => 'forms.first_attempt_help',
]);

$builder->add('unboxed', CheckboxType::class, [
'label' => 'forms.unboxed',
'required' => false,
'help' => 'forms.unboxed_help',
]);

$builder->add('puzzle', TextType::class, [
'label' => 'forms.puzzle',
'help' => 'forms.puzzle_help',
Expand Down
2 changes: 2 additions & 0 deletions src/Message/AddPuzzleSolvingTime.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ public function __construct(
public array $groupPlayers,
public null|DateTimeImmutable $finishedAt,
public bool $firstAttempt,
public bool $unboxed,
) {
}

Expand All @@ -45,6 +46,7 @@ public static function fromFormData(UuidInterface $timeId, string $userId, array
groupPlayers: $groupPlayers,
finishedAt: $data->finishedAt,
firstAttempt: $data->firstAttempt,
unboxed: $data->unboxed,
);
}
}
2 changes: 2 additions & 0 deletions src/Message/EditPuzzleSolvingTime.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ public function __construct(
public null|DateTimeImmutable $finishedAt,
public null|UploadedFile $finishedPuzzlesPhoto,
public bool $firstAttempt,
public bool $unboxed,
) {
}

Expand All @@ -40,6 +41,7 @@ public static function fromFormData(string $userId, string $timeId, array $group
finishedAt: $formData->finishedAt,
finishedPuzzlesPhoto: $formData->finishedPuzzlesPhoto,
firstAttempt: $formData->firstAttempt,
unboxed: $formData->unboxed,
);
}
}
1 change: 1 addition & 0 deletions src/MessageHandler/AddPuzzleSolvingTimeHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ public function __invoke(AddPuzzleSolvingTime $message): void
$message->comment,
$finishedPuzzlePhotoPath,
$message->firstAttempt,
$message->unboxed,
competition: $competition,
);

Expand Down
1 change: 1 addition & 0 deletions src/MessageHandler/AddPuzzleTrackingHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ public function __invoke(AddPuzzleTracking $message): void
comment: $message->comment,
finishedPuzzlePhoto: $finishedPuzzlePhotoPath,
firstAttempt: false,
unboxed: false,
);

$this->entityManager->persist($solvingTime);
Expand Down
1 change: 1 addition & 0 deletions src/MessageHandler/EditPuzzleSolvingTimeHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ public function __invoke(EditPuzzleSolvingTime $message): void
$finishedAt,
$finishedPuzzlePhotoPath,
$message->firstAttempt,
$message->unboxed,
competition: $competition,
);
}
Expand Down
2 changes: 2 additions & 0 deletions src/Query/GetExportableSolvingTimes.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ public function byPlayerId(string $playerId): array
pst.finished_at,
pst.tracked_at,
pst.first_attempt,
pst.unboxed,
pst.finished_puzzle_photo,
pst.comment,
pst.puzzling_type AS solving_type,
Expand Down Expand Up @@ -76,6 +77,7 @@ public function byPlayerId(string $playerId): array
* finished_at: string,
* tracked_at: string,
* first_attempt: bool,
* unboxed: bool,
* finished_puzzle_photo: null|string,
* comment: null|string,
* solving_type: string,
Expand Down
2 changes: 2 additions & 0 deletions src/Query/GetFastestGroups.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ public function perPiecesCount(int $piecesCount, int $howManyPlayers, null|Count
pst.id AS time_id,
pst.team ->> 'team_id' AS team_id,
first_attempt,
pst.unboxed,
player.is_private,
competition.id AS competition_id,
competition.shortcut AS competition_shortcut,
Expand Down Expand Up @@ -125,6 +126,7 @@ public function perPiecesCount(int $piecesCount, int $howManyPlayers, null|Count
* puzzle_identification_number: null|string,
* finished_at: string,
* first_attempt: bool,
* unboxed: bool,
* is_private: bool,
* competition_id: null|string,
* competition_name: null|string,
Expand Down
2 changes: 2 additions & 0 deletions src/Query/GetFastestPairs.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ public function perPiecesCount(int $piecesCount, int $howManyPlayers, null|Count
pst.id AS time_id,
pst.team ->> 'team_id' AS team_id,
first_attempt,
pst.unboxed,
player.is_private,
competition.id AS competition_id,
competition.shortcut AS competition_shortcut,
Expand Down Expand Up @@ -125,6 +126,7 @@ public function perPiecesCount(int $piecesCount, int $howManyPlayers, null|Count
* players: null|string,
* finished_at: string,
* first_attempt: bool,
* unboxed: bool,
* is_private: bool,
* competition_id: null|string,
* competition_name: null|string,
Expand Down
2 changes: 2 additions & 0 deletions src/Query/GetFastestPlayers.php
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ public function perPiecesCount(int $piecesCount, int $limit, null|CountryCode $c
puzzle_solving_time.id AS time_id,
puzzle.identification_number AS puzzle_identification_number,
puzzle_solving_time.first_attempt,
puzzle_solving_time.unboxed,
is_private,
competition.id AS competition_id,
competition.shortcut AS competition_shortcut,
Expand Down Expand Up @@ -114,6 +115,7 @@ public function perPiecesCount(int $piecesCount, int $limit, null|CountryCode $c
* puzzle_identification_number: null|string,
* finished_at: string,
* first_attempt: bool,
* unboxed: bool,
* is_private: bool,
* competition_id: null|string,
* competition_name: null|string,
Expand Down
Loading