From 695b193accaecd7968a305ae4c31c96bc4447841 Mon Sep 17 00:00:00 2001 From: SpeedyD Date: Thu, 27 Nov 2025 01:46:25 +0100 Subject: [PATCH 1/7] Feat(characters): Show only traits belonging to the character's species (or non-species specific traits) --- .../Characters/CharacterImageController.php | 2 +- .../Characters/DesignController.php | 15 +++++++- app/Models/Feature/Feature.php | 38 ++++++++++++++++--- config/lorekeeper/extensions.php | 3 ++ 4 files changed, 50 insertions(+), 8 deletions(-) diff --git a/app/Http/Controllers/Admin/Characters/CharacterImageController.php b/app/Http/Controllers/Admin/Characters/CharacterImageController.php index 0aefec61b3..f537c2e659 100644 --- a/app/Http/Controllers/Admin/Characters/CharacterImageController.php +++ b/app/Http/Controllers/Admin/Characters/CharacterImageController.php @@ -106,7 +106,7 @@ public function getEditImageFeatures($id) { 'rarities' => ['0' => 'Select Rarity'] + Rarity::orderBy('sort', 'DESC')->pluck('name', 'id')->toArray(), 'specieses' => ['0' => 'Select Species'] + Species::orderBy('sort', 'DESC')->pluck('name', 'id')->toArray(), 'subtypes' => Subtype::where('species_id', '=', $image->species_id)->orderBy('sort', 'DESC')->pluck('name', 'id')->toArray(), - 'features' => Feature::getDropdownItems(1), + 'features' => Feature::getDropdownItems(Auth::user()->hasPower('edit_data'), $image->species_id), ]); } diff --git a/app/Http/Controllers/Characters/DesignController.php b/app/Http/Controllers/Characters/DesignController.php index 58ea7a54b1..adf6dae147 100644 --- a/app/Http/Controllers/Characters/DesignController.php +++ b/app/Http/Controllers/Characters/DesignController.php @@ -235,7 +235,7 @@ public function getFeatures($id) { 'specieses' => ['0' => 'Select Species'] + Species::visible()->orderBy('sort', 'DESC')->pluck('name', 'id')->toArray(), 'subtypes' => Subtype::visible()->where('species_id', '=', $r->species_id)->orderBy('sort', 'DESC')->pluck('name', 'id')->toArray(), 'rarities' => ['0' => 'Select Rarity'] + Rarity::orderBy('sort', 'DESC')->pluck('name', 'id')->toArray(), - 'features' => Feature::getDropdownItems(), + 'features' => Feature::getDropdownItems(Auth::user()->hasPower('edit_data'), $r->species_id), ]); } @@ -254,6 +254,19 @@ public function getFeaturesSubtype(Request $request) { ]); } + /** + * Shows the edit image trait portion of the modal. + * + * @return \Illuminate\Contracts\Support\Renderable + */ + public function getFeaturesFeature(Request $request) { + $species = $request->input('species'); + + return view('character.design._features_feature', [ + 'features' => Feature::getDropdownItems(Auth::user()->hasPower('edit_data'), $species) + ]); + } + /** * Edits a design update request's features section. * diff --git a/app/Models/Feature/Feature.php b/app/Models/Feature/Feature.php index 253389abd8..d0f1b70563 100644 --- a/app/Models/Feature/Feature.php +++ b/app/Models/Feature/Feature.php @@ -6,6 +6,7 @@ use App\Models\Rarity; use App\Models\Species\Species; use App\Models\Species\Subtype; +use Illuminate\Database\Eloquent\Builder; use Illuminate\Support\Facades\DB; class Feature extends Model { @@ -286,7 +287,7 @@ public function getAdminPowerAttribute() { **********************************************************************************************/ - public static function getDropdownItems($withHidden = 0) { + public static function getDropdownItems($withHidden = 0, $withSpecies = 0) { $visibleOnly = 1; if ($withHidden) { $visibleOnly = 0; @@ -295,10 +296,21 @@ public static function getDropdownItems($withHidden = 0) { if (config('lorekeeper.extensions.organised_traits_dropdown.enable')) { $sorted_feature_categories = collect(FeatureCategory::all()->where('is_visible', '>=', $visibleOnly)->sortBy('sort')->pluck('name')->toArray()); - $grouped = self::where('is_visible', '>=', $visibleOnly) - ->select('name', 'id', 'feature_category_id', 'rarity_id', 'species_id', 'subtype_id')->with(['category', 'rarity', 'species', 'subtype']) - ->orderBy('name')->get()->keyBy('id')->groupBy('category.name', $preserveKeys = true) - ->toArray(); + if (config('show_species-only_traits_in_dropdown') && $withSpecies) { + $grouped = self::where('is_visible', '>=', $visibleOnly) + ->when($withSpecies, function (Builder $query, int $withSpecies) { + $query->where('species_id', '=', $withSpecies) + ->orWhere('species_id', '=', NULL); + }) + ->select('name', 'id', 'feature_category_id', 'rarity_id', 'species_id', 'subtype_id')->with(['category', 'rarity', 'species', 'subtype']) + ->orderBy('name')->get()->keyBy('id')->groupBy('category.name', $preserveKeys = true) + ->toArray(); + } else { + $grouped = self::where('is_visible', '>=', $visibleOnly) + ->select('name', 'id', 'feature_category_id', 'rarity_id', 'species_id', 'subtype_id')->with(['category', 'rarity', 'species', 'subtype']) + ->orderBy('name')->get()->keyBy('id')->groupBy('category.name', $preserveKeys = true) + ->toArray(); + } if (isset($grouped[''])) { if (!$sorted_feature_categories->contains('Miscellaneous')) { $sorted_feature_categories->push('Miscellaneous'); @@ -353,7 +365,21 @@ public static function getDropdownItems($withHidden = 0) { return $features_by_category; } else { - return self::where('is_visible', '>=', $visibleOnly)->orderBy('name')->pluck('name', 'id')->toArray(); + if (config('show_species-only_traits_in_dropdown') && $withSpecies) { + return self::where('is_visible', '>=', $visibleOnly) + ->when($withSpecies, function (Builder $query, int $withSpecies) { + $query->where('species_id', '=', $withSpecies) + ->orWhere('species_id', '=', NULL); + }) + ->orderBy('name') + ->pluck('name', 'id') + ->toArray(); + } else { + return self::where('is_visible', '>=', $visibleOnly) + ->orderBy('name') + ->pluck('name', 'id') + ->toArray(); + } } } } diff --git a/config/lorekeeper/extensions.php b/config/lorekeeper/extensions.php index 9e0f7041b2..818e7c07ac 100644 --- a/config/lorekeeper/extensions.php +++ b/config/lorekeeper/extensions.php @@ -140,4 +140,7 @@ // Unmerge Item Page and Item Entry - Speedy 'unmerge_item_page_and_entry' => 0, // If enabled, uses the html on world/item_page.blade.php instead of the include that links to world/_item_entry.blade.php + + // Show Species-only traits in dropdown - Speedy + 'show_species-only_traits_in_dropdown' => 0, // If enabled, will only show traits from the associated species as well as traits that aren't species-limited in the dropdown menus. ]; From 50d27bd4bb88d44cb5850a58bc9197c5ef1e9b28 Mon Sep 17 00:00:00 2001 From: SpeedyD Date: Thu, 27 Nov 2025 01:58:24 +0100 Subject: [PATCH 2/7] Refresh trait on species change --- .../admin/_edit_features_modal.blade.php | 17 +++++++++++++++- .../design/_features_feature.blade.php | 3 +++ .../views/character/design/features.blade.php | 20 ++++++++++++++++++- .../views/widgets/_image_upload_js.blade.php | 1 + routes/lorekeeper/members.php | 1 + 5 files changed, 40 insertions(+), 2 deletions(-) create mode 100644 resources/views/character/design/_features_feature.blade.php diff --git a/resources/views/character/admin/_edit_features_modal.blade.php b/resources/views/character/admin/_edit_features_modal.blade.php index 13affc9d08..9f0dc59dfd 100644 --- a/resources/views/character/admin/_edit_features_modal.blade.php +++ b/resources/views/character/admin/_edit_features_modal.blade.php @@ -26,7 +26,7 @@ @endforeach -
+
{!! Form::select('feature_id[]', $features, null, ['class' => 'form-control mr-2 feature-select', 'placeholder' => 'Select Trait']) !!} {!! Form::text('feature_data[]', null, ['class' => 'form-control mr-2', 'placeholder' => 'Extra Info (Optional)']) !!} × @@ -116,6 +116,7 @@ function featureSelectedRender(item, escape) { $("#species").change(function() { refreshSubtype(); + refreshTrait(); }); function refreshSubtype() { @@ -135,6 +136,20 @@ function refreshSubtype() { }); }; + + function refreshTrait() { + var species = $('#species').val(); + $.ajax({ + type: "GET", + url: "{{ url('designs/traits/feature') }}?species=" + species, + dataType: "text" + }).done(function(res) { + $("#new-feature").html(res); + }).fail(function(jqXHR, textStatus, errorThrown) { + alert("AJAX call failed: " + textStatus + ", " + errorThrown); + }); + }; + $("#subtype").selectize({ maxItems: {{ config('lorekeeper.extensions.multiple_subtype_limit') }}, }); diff --git a/resources/views/character/design/_features_feature.blade.php b/resources/views/character/design/_features_feature.blade.php new file mode 100644 index 0000000000..b9b7fd5e74 --- /dev/null +++ b/resources/views/character/design/_features_feature.blade.php @@ -0,0 +1,3 @@ +{!! Form::select('feature_id[]', $features, null, ['class' => 'form-control mr-2 feature-select', 'placeholder' => 'Select Trait']) !!} +{!! Form::text('feature_data[]', null, ['class' => 'form-control mr-2', 'placeholder' => 'Extra Info (Optional)']) !!} +× \ No newline at end of file diff --git a/resources/views/character/design/features.blade.php b/resources/views/character/design/features.blade.php index 08c8d3866a..a1ce6d562d 100644 --- a/resources/views/character/design/features.blade.php +++ b/resources/views/character/design/features.blade.php @@ -73,7 +73,7 @@ @endforeach @endif
-
+
{!! Form::select('feature_id[]', $features, null, ['class' => 'form-control mr-2 feature-select', 'placeholder' => 'Select Trait']) !!} {!! Form::text('feature_data[]', null, ['class' => 'form-control mr-2', 'placeholder' => 'Extra Info (Optional)']) !!} × @@ -144,6 +144,11 @@