mirror of
https://github.com/AnimeThemes/animethemes-server.git
synced 2026-07-11 01:24:46 +02:00
feat: added better labels & fixed filament search (#799)
This commit is contained in:
@@ -13,6 +13,7 @@ use App\Actions\Models\Wiki\Anime\ApiAction\MalAnimeApiAction;
|
||||
use App\Concerns\Models\CanCreateAnimeSynonym;
|
||||
use App\Concerns\Models\CanCreateStudio;
|
||||
use App\Enums\Actions\ActionStatus;
|
||||
use App\Enums\Models\Wiki\AnimeSynonymType;
|
||||
use App\Models\Wiki\Anime;
|
||||
use Exception;
|
||||
use Illuminate\Support\Arr;
|
||||
@@ -141,8 +142,15 @@ class BackfillAnimeAction extends BackfillWikiAction
|
||||
{
|
||||
if (!$this->toBackfill[self::SYNONYMS]) return;
|
||||
|
||||
$texts = [];
|
||||
foreach ($api->getSynonyms() as $type => $text) {
|
||||
if ($type === AnimeSynonymType::OTHER->value && in_array($text, $texts, true)) {
|
||||
Log::info("Skipping duplicate synonym '$text' for Anime {$this->getModel()->getName()}");
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->createAnimeSynonym($text, $type, $this->getModel());
|
||||
$texts[] = $text;
|
||||
}
|
||||
|
||||
if ($this->getModel()->animesynonyms()->exists()) {
|
||||
|
||||
@@ -90,7 +90,7 @@ class BelongsTo extends ComponentsSelect
|
||||
if (in_array(Searchable::class, class_uses_recursive($model))) {
|
||||
return $this
|
||||
->getSearchResultsUsing(function (string $search) use ($model) {
|
||||
$search = preg_replace('/[^A-Za-z0-9 ]/', '', $search);
|
||||
$search = $this->escapeReservedChars($search);
|
||||
/** @phpstan-ignore-next-line */
|
||||
return $model::search($search)
|
||||
->take(25)
|
||||
@@ -125,4 +125,25 @@ class BelongsTo extends ComponentsSelect
|
||||
->with('image', $model instanceof User ? $model->getFilamentAvatarUrl() : null)
|
||||
->render();
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare the search query for Elasticsearch.
|
||||
*
|
||||
* @param string $search
|
||||
* @return string
|
||||
*/
|
||||
public function escapeReservedChars(string $search) : string
|
||||
{
|
||||
return preg_replace(
|
||||
[
|
||||
'_[<>]+_',
|
||||
'_[-+=!(){}[\]^"~*?:\\/\\\\]|&(?=&)|\|(?=\|)_',
|
||||
],
|
||||
[
|
||||
'',
|
||||
'\\\\$0',
|
||||
],
|
||||
$search
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ class Select extends ComponentsSelect
|
||||
->searchable()
|
||||
->getOptionLabelUsing(fn ($state) => BelongsTo::getSearchLabelWithBlade($model::find($state)))
|
||||
->getSearchResultsUsing(function (string $search) use ($livewire, $model, $loadRelation) {
|
||||
$search = preg_replace('/[^A-Za-z0-9 ]/', '', $search);
|
||||
$search = $this->escapeReservedChars($search);
|
||||
/** @phpstan-ignore-next-line */
|
||||
return $model::search($search)
|
||||
->query(function (Builder $query) use ($livewire) {
|
||||
@@ -54,4 +54,25 @@ class Select extends ComponentsSelect
|
||||
|
||||
return $this->searchable();
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare the search query for Elasticsearch.
|
||||
*
|
||||
* @param string $search
|
||||
* @return string
|
||||
*/
|
||||
public function escapeReservedChars(string $search) : string
|
||||
{
|
||||
return preg_replace(
|
||||
[
|
||||
'_[<>]+_',
|
||||
'_[-+=!(){}[\]^"~*?:\\/\\\\]|&(?=&)|\|(?=\|)_',
|
||||
],
|
||||
[
|
||||
'',
|
||||
'\\\\$0',
|
||||
],
|
||||
$search
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -52,9 +52,11 @@ class DateFilter extends Filter
|
||||
->schema([
|
||||
DatePicker::make($this->getName().'_'.'from')
|
||||
->label($this->fromLabel)
|
||||
->native(false)
|
||||
->required(),
|
||||
DatePicker::make($this->getName().'_'.'to')
|
||||
->label($this->toLabel)
|
||||
->native(false)
|
||||
->required(),
|
||||
]),
|
||||
])
|
||||
|
||||
@@ -35,7 +35,7 @@ class GlobalSearchScoutProvider implements GlobalSearchProvider
|
||||
continue;
|
||||
}
|
||||
|
||||
$query = preg_replace('/[^A-Za-z0-9 ]/', '', $query);
|
||||
$query = $this->escapeReservedChars($query);
|
||||
|
||||
/** @var ScoutBuilder $scoutBuilder */
|
||||
$scoutBuilder = $resource::getModel()::search($query);
|
||||
@@ -68,4 +68,25 @@ class GlobalSearchScoutProvider implements GlobalSearchProvider
|
||||
|
||||
return $builder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare the search query for Elasticsearch.
|
||||
*
|
||||
* @param string $search
|
||||
* @return string
|
||||
*/
|
||||
public function escapeReservedChars(string $search) : string
|
||||
{
|
||||
return preg_replace(
|
||||
[
|
||||
'_[<>]+_',
|
||||
'_[-+=!(){}[\]^"~*?:\\/\\\\]|&(?=&)|\|(?=\|)_',
|
||||
],
|
||||
[
|
||||
'',
|
||||
'\\\\$0',
|
||||
],
|
||||
$search
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -136,30 +136,16 @@ class FeaturedTheme extends BaseResource
|
||||
DatePicker::make(FeaturedThemeModel::ATTRIBUTE_START_AT)
|
||||
->label(__('filament.fields.featured_theme.start_at.name'))
|
||||
->helperText(__('filament.fields.featured_theme.start_at.help'))
|
||||
->native(false)
|
||||
->required()
|
||||
->rules([
|
||||
'required',
|
||||
str('date_format:')
|
||||
->append(implode(',', $allowedDateFormats))
|
||||
->__toString(),
|
||||
str('after:')
|
||||
->append(FeaturedThemeModel::ATTRIBUTE_START_AT)
|
||||
->__toString(),
|
||||
]),
|
||||
->before(FeaturedThemeModel::ATTRIBUTE_END_AT),
|
||||
|
||||
DatePicker::make(FeaturedThemeModel::ATTRIBUTE_END_AT)
|
||||
->label(__('filament.fields.featured_theme.end_at.name'))
|
||||
->helperText(__('filament.fields.featured_theme.end_at.help'))
|
||||
->native(false)
|
||||
->required()
|
||||
->rules([
|
||||
'required',
|
||||
str('date_format:')
|
||||
->append(implode(',', $allowedDateFormats))
|
||||
->__toString(),
|
||||
str('after:')
|
||||
->append(FeaturedThemeModel::ATTRIBUTE_START_AT)
|
||||
->__toString(),
|
||||
]),
|
||||
->after(FeaturedThemeModel::ATTRIBUTE_START_AT),
|
||||
|
||||
BelongsTo::make(FeaturedThemeModel::ATTRIBUTE_ENTRY)
|
||||
->resource(EntryResource::class)
|
||||
|
||||
@@ -6,6 +6,8 @@ namespace App\Filament\Resources\Base;
|
||||
|
||||
use App\Filament\HeaderActions\Base\CreateHeaderAction;
|
||||
use Filament\Resources\Pages\ListRecords;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
/**
|
||||
* Class BaseListResources.
|
||||
@@ -25,4 +27,51 @@ abstract class BaseListResources extends ListRecords
|
||||
CreateHeaderAction::make(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Using Laravel Scout to search.
|
||||
*
|
||||
* @param Builder $query
|
||||
* @param class-string<Model> $modelClass
|
||||
* @return Builder
|
||||
*/
|
||||
protected function makeScout(Builder $query, string $modelClass): Builder
|
||||
{
|
||||
$this->applyColumnSearchesToTableQuery($query);
|
||||
|
||||
$model = new $modelClass;
|
||||
|
||||
if (filled($search = $this->getTableSearch())) {
|
||||
$search = $this->escapeReservedChars($search);
|
||||
/** @phpstan-ignore-next-line */
|
||||
$keys = $modelClass::search($search)->take(25)->keys();
|
||||
|
||||
$query
|
||||
->whereIn($model->getKeyName(), $keys)
|
||||
->orderByRaw("FIELD({$this->getResource()::getRecordRouteKeyName()}, " . $keys->implode(',') . ')');
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare the search query for Elasticsearch.
|
||||
*
|
||||
* @param string $search
|
||||
* @return string
|
||||
*/
|
||||
public function escapeReservedChars(string $search) : string
|
||||
{
|
||||
return preg_replace(
|
||||
[
|
||||
'_[<>]+_',
|
||||
'_[-+=!(){}[\]^"~*?:\\/\\\\]|&(?=&)|\|(?=\|)_',
|
||||
],
|
||||
[
|
||||
'',
|
||||
'\\\\$0',
|
||||
],
|
||||
$search
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,7 +81,8 @@ abstract class BaseResource extends Resource
|
||||
public static function table(Table $table): Table
|
||||
{
|
||||
return $table
|
||||
->defaultSort(static::getRecordRouteKeyName(), 'desc')
|
||||
->striped()
|
||||
->defaultSort(fn (Table $table) => $table->hasSearch() ? null : static::getRecordRouteKeyName(), fn (Table $table) => $table->hasSearch() ? null : 'desc')
|
||||
->filters(static::getFilters())
|
||||
->filtersFormMaxHeight('400px')
|
||||
->actions(static::getActions())
|
||||
@@ -89,7 +90,9 @@ abstract class BaseResource extends Resource
|
||||
->headerActions(static::getTableActions())
|
||||
->recordUrl(fn (Model $record): string => static::getUrl('view', ['record' => $record]))
|
||||
->paginated([10, 25, 50, 100, 'all'])
|
||||
->defaultPaginationPageOption(25);
|
||||
->defaultPaginationPageOption(25)
|
||||
->extremePaginationLinks()
|
||||
->deferLoading(!app()->runningUnitTests());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -38,13 +38,6 @@ class ListExternalProfiles extends BaseListResources
|
||||
*/
|
||||
protected function applySearchToTableQuery(Builder $query): Builder
|
||||
{
|
||||
$this->applyColumnSearchesToTableQuery($query);
|
||||
|
||||
if (filled($search = $this->getTableSearch())) {
|
||||
$search = preg_replace('/[^A-Za-z0-9 ]/', '', $search);
|
||||
$query->whereIn(ExternalProfileModel::ATTRIBUTE_ID, ExternalProfileModel::search($search)->take(25)->keys());
|
||||
}
|
||||
|
||||
return $query;
|
||||
return $this->makeScout($query, ExternalProfileModel::class);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,13 +38,6 @@ class ListPlaylists extends BaseListResources
|
||||
*/
|
||||
protected function applySearchToTableQuery(Builder $query): Builder
|
||||
{
|
||||
$this->applyColumnSearchesToTableQuery($query);
|
||||
|
||||
if (filled($search = $this->getTableSearch())) {
|
||||
$search = preg_replace('/[^A-Za-z0-9 ]/', '', $search);
|
||||
$query->whereIn(PlaylistModel::ATTRIBUTE_ID, PlaylistModel::search($search)->take(25)->keys());
|
||||
}
|
||||
|
||||
return $query;
|
||||
return $this->makeScout($query, PlaylistModel::class);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,14 +55,7 @@ class ListAnimes extends BaseListResources
|
||||
*/
|
||||
protected function applySearchToTableQuery(Builder $query): Builder
|
||||
{
|
||||
$this->applyColumnSearchesToTableQuery($query);
|
||||
|
||||
if (filled($search = $this->getTableSearch())) {
|
||||
$search = preg_replace('/[^A-Za-z0-9 ]/', '', $search);
|
||||
$query->whereIn(AnimeModel::ATTRIBUTE_ID, AnimeModel::search($search)->take(25)->keys());
|
||||
}
|
||||
|
||||
return $query;
|
||||
return $this->makeScout($query, AnimeModel::class);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -38,13 +38,6 @@ class ListSynonyms extends BaseListResources
|
||||
*/
|
||||
protected function applySearchToTableQuery(Builder $query): Builder
|
||||
{
|
||||
$this->applyColumnSearchesToTableQuery($query);
|
||||
|
||||
if (filled($search = $this->getTableSearch())) {
|
||||
$search = preg_replace('/[^A-Za-z0-9 ]/', '', $search);
|
||||
$query->whereIn(AnimeSynonym::ATTRIBUTE_ID, AnimeSynonym::search($search)->take(25)->keys());
|
||||
}
|
||||
|
||||
return $query;
|
||||
return $this->makeScout($query, AnimeSynonym::class);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,11 +42,11 @@ use Filament\Infolists\Components\RepeatableEntry;
|
||||
use Filament\Infolists\Components\Section;
|
||||
use Filament\Infolists\Infolist;
|
||||
use Filament\Resources\RelationManagers\RelationGroup;
|
||||
use Filament\Tables\Filters\Filter;
|
||||
use Filament\Tables\Filters\SelectFilter;
|
||||
use Filament\Tables\Table;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Validation\Rules\Enum;
|
||||
|
||||
@@ -155,7 +155,11 @@ class Theme extends BaseResource
|
||||
$query = parent::getEloquentQuery();
|
||||
|
||||
// Necessary to prevent lazy loading when loading related resources
|
||||
return $query->with([AnimeTheme::RELATION_ANIME, AnimeTheme::RELATION_SONG, AnimeTheme::RELATION_GROUP]);
|
||||
return $query->with([
|
||||
AnimeTheme::RELATION_ANIME,
|
||||
AnimeTheme::RELATION_SONG,
|
||||
AnimeTheme::RELATION_GROUP
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -396,6 +400,11 @@ class Theme extends BaseResource
|
||||
NumberFilter::make(ThemeModel::ATTRIBUTE_SEQUENCE)
|
||||
->label(__('filament.fields.anime_theme.sequence.name')),
|
||||
|
||||
Filter::make(ThemeType::IN->localize())
|
||||
->label(__('filament.filters.anime_theme.without_in'))
|
||||
->query(fn (Builder $query) => $query->whereNot(ThemeModel::ATTRIBUTE_TYPE, ThemeType::IN->value))
|
||||
->default(true),
|
||||
|
||||
...parent::getFilters(),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\Resources\Wiki\Anime\Theme;
|
||||
|
||||
use App\Enums\Models\Wiki\ThemeType;
|
||||
use App\Filament\Components\Columns\BelongsToColumn;
|
||||
use App\Filament\Components\Columns\TextColumn;
|
||||
use App\Filament\Components\Fields\BelongsTo;
|
||||
@@ -33,6 +34,7 @@ use Filament\Infolists\Components\Section;
|
||||
use Filament\Infolists\Infolist;
|
||||
use Filament\Resources\RelationManagers\RelationGroup;
|
||||
use Filament\Tables\Columns\IconColumn;
|
||||
use Filament\Tables\Filters\Filter;
|
||||
use Filament\Tables\Table;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
@@ -336,6 +338,11 @@ class Entry extends BaseResource
|
||||
CheckboxFilter::make(EntryModel::ATTRIBUTE_SPOILER)
|
||||
->label(__('filament.fields.anime_theme_entry.spoiler.name')),
|
||||
|
||||
Filter::make(ThemeType::IN->localize())
|
||||
->label(__('filament.filters.anime_theme.without_in'))
|
||||
->query(fn (Builder $query) => $query->whereDoesntHaveRelation(AnimeThemeEntry::RELATION_THEME, ThemeModel::ATTRIBUTE_TYPE, ThemeType::IN->value))
|
||||
->default(true),
|
||||
|
||||
...parent::getFilters(),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -38,13 +38,6 @@ class ListEntries extends BaseListResources
|
||||
*/
|
||||
protected function applySearchToTableQuery(Builder $query): Builder
|
||||
{
|
||||
$this->applyColumnSearchesToTableQuery($query);
|
||||
|
||||
if (filled($search = $this->getTableSearch())) {
|
||||
$search = preg_replace('/[^A-Za-z0-9 ]/', '', $search);
|
||||
$query->whereIn(AnimeThemeEntry::ATTRIBUTE_ID, AnimeThemeEntry::search($search)->take(25)->keys());
|
||||
}
|
||||
|
||||
return $query;
|
||||
return $this->makeScout($query, AnimeThemeEntry::class);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,13 +38,6 @@ class ListThemes extends BaseListResources
|
||||
*/
|
||||
protected function applySearchToTableQuery(Builder $query): Builder
|
||||
{
|
||||
$this->applyColumnSearchesToTableQuery($query);
|
||||
|
||||
if (filled($search = $this->getTableSearch())) {
|
||||
$search = preg_replace('/[^A-Za-z0-9 ]/', '', $search);
|
||||
$query->whereIn(AnimeTheme::ATTRIBUTE_ID, AnimeTheme::search($search)->take(25)->keys());
|
||||
}
|
||||
|
||||
return $query;
|
||||
return $this->makeScout($query, AnimeTheme::class);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,14 +54,7 @@ class ListArtists extends BaseListResources
|
||||
*/
|
||||
protected function applySearchToTableQuery(Builder $query): Builder
|
||||
{
|
||||
$this->applyColumnSearchesToTableQuery($query);
|
||||
|
||||
if (filled($search = $this->getTableSearch())) {
|
||||
$search = preg_replace('/[^A-Za-z0-9 ]/', '', $search);
|
||||
$query->whereIn(ArtistModel::ATTRIBUTE_ID, ArtistModel::search($search)->take(25)->keys());
|
||||
}
|
||||
|
||||
return $query;
|
||||
return $this->makeScout($query, ArtistModel::class);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -38,13 +38,6 @@ class ListSeries extends BaseListResources
|
||||
*/
|
||||
protected function applySearchToTableQuery(Builder $query): Builder
|
||||
{
|
||||
$this->applyColumnSearchesToTableQuery($query);
|
||||
|
||||
if (filled($search = $this->getTableSearch())) {
|
||||
$search = preg_replace('/[^A-Za-z0-9 ]/', '', $search);
|
||||
$query->whereIn(SeriesModel::ATTRIBUTE_ID, SeriesModel::search($search)->take(25)->keys());
|
||||
}
|
||||
|
||||
return $query;
|
||||
return $this->makeScout($query, SeriesModel::class);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,14 +49,7 @@ class ListSongs extends BaseListResources
|
||||
*/
|
||||
protected function applySearchToTableQuery(Builder $query): Builder
|
||||
{
|
||||
$this->applyColumnSearchesToTableQuery($query);
|
||||
|
||||
if (filled($search = $this->getTableSearch())) {
|
||||
$search = preg_replace('/[^A-Za-z0-9 ]/', '', $search);
|
||||
$query->whereIn(SongModel::ATTRIBUTE_ID, SongModel::search($search)->take(25)->keys());
|
||||
}
|
||||
|
||||
return $query;
|
||||
return $this->makeScout($query, SongModel::class);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -412,6 +412,8 @@ class Performance extends BaseResource
|
||||
|
||||
Repeater::make('memberships')
|
||||
->label(__('filament.resources.label.memberships'))
|
||||
->helperText(__('filament.fields.performance.memberships.help'))
|
||||
->addActionLabel(__('filament.fields.performance.memberships.add'))
|
||||
->collapsible()
|
||||
->defaultItems(0)
|
||||
->columns(3)
|
||||
@@ -420,6 +422,7 @@ class Performance extends BaseResource
|
||||
BelongsTo::make(Membership::ATTRIBUTE_MEMBER)
|
||||
->resource(ArtistResource::class)
|
||||
->showCreateOption()
|
||||
->label(__('filament.fields.membership.member'))
|
||||
->required()
|
||||
->rules(['required']),
|
||||
|
||||
|
||||
@@ -50,14 +50,7 @@ class ListStudios extends BaseListResources
|
||||
*/
|
||||
protected function applySearchToTableQuery(Builder $query): Builder
|
||||
{
|
||||
$this->applyColumnSearchesToTableQuery($query);
|
||||
|
||||
if (filled($search = $this->getTableSearch())) {
|
||||
$search = preg_replace('/[^A-Za-z0-9 ]/', '', $search);
|
||||
$query->whereIn(StudioModel::ATTRIBUTE_ID, StudioModel::search($search)->take(25)->keys());
|
||||
}
|
||||
|
||||
return $query;
|
||||
return $this->makeScout($query, StudioModel::class);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -45,14 +45,7 @@ class ListVideos extends BaseListResources
|
||||
*/
|
||||
protected function applySearchToTableQuery(Builder $query): Builder
|
||||
{
|
||||
$this->applyColumnSearchesToTableQuery($query);
|
||||
|
||||
if (filled($search = $this->getTableSearch())) {
|
||||
$search = preg_replace('/[^A-Za-z0-9 ]/', '', $search);
|
||||
$query->whereIn(VideoModel::ATTRIBUTE_ID, VideoModel::search($search)->take(25)->keys());
|
||||
}
|
||||
|
||||
return $query;
|
||||
return $this->makeScout($query, VideoModel::class);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -149,7 +149,12 @@ class AnimeThemeEntry extends BaseModel implements InteractsWithSchema
|
||||
public function toSearchableArray(): array
|
||||
{
|
||||
$array = $this->toArray();
|
||||
$array['theme'] = $this->animetheme->toSearchableArray();
|
||||
|
||||
$theme = is_null($this->animetheme)
|
||||
? $this->animetheme()->withoutGlobalScope(WithoutInsertSongScope::class)->first()
|
||||
: $this->animetheme;
|
||||
|
||||
$array['theme'] = $theme->toSearchableArray();
|
||||
|
||||
// Overwrite version with readable format "V{#}"
|
||||
$array['version'] = Str::of(empty($this->version) ? '1' : $this->version)->prepend('V')->__toString();
|
||||
|
||||
@@ -735,11 +735,11 @@ return [
|
||||
],
|
||||
'membership' => [
|
||||
'alias' => [
|
||||
'help' => 'Used in place of the Artist if the performance is using an alias.',
|
||||
'help' => 'Used in place of the Member if the performance is using an alias.',
|
||||
'name' => 'Alias',
|
||||
],
|
||||
'as' => [
|
||||
'help' => 'Used alongside the Artist name if the performance is made as a character.',
|
||||
'help' => 'Used alongside the Member name if the performance is made as a character.',
|
||||
'name' => 'As',
|
||||
],
|
||||
'group' => 'Group',
|
||||
@@ -776,6 +776,10 @@ return [
|
||||
'load_members' => [
|
||||
'name' => 'Load Members',
|
||||
],
|
||||
'memberships' => [
|
||||
'help' => 'When a group and its members are credited, you need to add the members (called memberships) that performed the song.',
|
||||
'add' => 'Add Member',
|
||||
],
|
||||
],
|
||||
'permission' => [
|
||||
'name' => 'Name',
|
||||
@@ -954,6 +958,9 @@ return [
|
||||
'from' => 'From',
|
||||
'to' => 'To',
|
||||
],
|
||||
'anime_theme' => [
|
||||
'without_in' => 'Without Insert Songs',
|
||||
],
|
||||
],
|
||||
'resources' => [
|
||||
'group' => [
|
||||
|
||||
Reference in New Issue
Block a user