feat(pokemon): remove manual sorting and enforce ID-based order

Remove pokemon.order permission and related API endpoints
Update queries to sort Pokemon by internal ID ascending
Replace reorderable list with standard list in Admin view
This commit is contained in:
2026-05-06 22:35:46 +08:00
parent df78685dc3
commit 82f08c1684
6 changed files with 24 additions and 69 deletions

View File

@@ -231,7 +231,6 @@ VALUES
('pokemon.create', 'Create Pokemon', 'Create Pokemon records.', 'Pokemon', true),
('pokemon.update', 'Update Pokemon', 'Edit Pokemon records.', 'Pokemon', true),
('pokemon.delete', 'Delete Pokemon', 'Delete Pokemon records.', 'Pokemon', true),
('pokemon.order', 'Order Pokemon', 'Reorder Pokemon records.', 'Pokemon', true),
('pokemon.fetch', 'Fetch Pokemon data', 'Fetch Pokemon data and sprite candidates.', 'Pokemon', true),
('pokemon.upload', 'Upload Pokemon images', 'Upload Pokemon images.', 'Pokemon', true),
('habitats.create', 'Create habitats', 'Create habitat records.', 'Habitats', true),
@@ -275,6 +274,9 @@ VALUES
('discussions.comments.like', 'Like discussion comments', 'Like and unlike entity discussion comments.', 'Discussions', true)
ON CONFLICT (key) DO NOTHING;
DELETE FROM permissions
WHERE key = 'pokemon.order';
INSERT INTO roles (key, name, description, level, enabled, system_role)
VALUES
('owner', 'Owner', 'Highest-level system owner with all permissions.', 1000, true, true),
@@ -329,7 +331,6 @@ JOIN permissions p ON p.key = ANY (ARRAY[
'pokemon.create',
'pokemon.update',
'pokemon.delete',
'pokemon.order',
'pokemon.fetch',
'pokemon.upload',
'habitats.create',
@@ -411,7 +412,6 @@ JOIN permissions p ON p.key = ANY (ARRAY[
'checklist.order',
'pokemon.create',
'pokemon.update',
'pokemon.order',
'pokemon.fetch',
'pokemon.upload',
'habitats.create',

View File

@@ -108,7 +108,7 @@ type ConfigDefinition = {
hasRateable?: boolean;
hasChangeLog?: boolean;
};
type SortableContentType = 'pokemon' | 'items' | 'ancient-artifacts' | 'recipes' | 'habitats';
type SortableContentType = 'items' | 'ancient-artifacts' | 'recipes' | 'habitats';
type SortableContentDefinition = {
table: string;
entityType: SortableContentType;
@@ -691,7 +691,6 @@ const configDefinitions: Record<ConfigType, ConfigDefinition> = {
};
const sortableContentDefinitions: Record<SortableContentType, SortableContentDefinition> = {
pokemon: { table: 'pokemon', entityType: 'pokemon' },
items: { table: 'items', entityType: 'items' },
'ancient-artifacts': { table: 'items', entityType: 'ancient-artifacts' },
recipes: { table: 'recipes', entityType: 'recipes' },
@@ -2809,7 +2808,7 @@ export async function globalSearch(paramsQuery: QueryParams = {}, locale = defau
${pokemonImageJson('p')} AS image
FROM pokemon p
WHERE ${pokemonName} ILIKE $1
ORDER BY ${orderByEntity('p')}
ORDER BY p.id
LIMIT $2
`,
[pattern, limit]
@@ -5746,11 +5745,6 @@ async function reorderContent(type: SortableContentType, payload: Record<string,
});
}
export async function reorderPokemon(payload: Record<string, unknown>, userId: number, locale = defaultLocale) {
await reorderContent('pokemon', payload, userId);
return listPokemon({}, locale);
}
export async function reorderItems(payload: Record<string, unknown>, userId: number, locale = defaultLocale) {
await reorderContent('items', payload, userId);
return listItems({}, locale);
@@ -5822,7 +5816,7 @@ export async function listPokemon(paramsQuery: QueryParams, locale = defaultLoca
}
const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';
return queryMaybePaged(`${pokemonProjection(locale)} ${whereClause} ORDER BY ${orderByEntity('p')}`, params, paramsQuery);
return queryMaybePaged(`${pokemonProjection(locale)} ${whereClause} ORDER BY p.id`, params, paramsQuery);
}
export async function getPokemon(id: number, locale = defaultLocale) {
@@ -5927,7 +5921,6 @@ export async function getPokemon(id: number, locale = defaultLocale) {
scored_pokemon AS (
SELECT
related_pokemon.id,
related_pokemon.sort_order,
(related_pokemon.environment_id = current_pokemon.environment_id) AS "environmentMatches",
COUNT(current_favourites.favorite_thing_id)::integer AS "favoriteThingMatchCount"
FROM current_pokemon
@@ -5936,7 +5929,7 @@ export async function getPokemon(id: number, locale = defaultLocale) {
ON related_pokemon_favourite.pokemon_id = related_pokemon.id
LEFT JOIN current_favourites
ON current_favourites.favorite_thing_id = related_pokemon_favourite.favorite_thing_id
GROUP BY related_pokemon.id, related_pokemon.sort_order, related_pokemon.environment_id, current_pokemon.environment_id
GROUP BY related_pokemon.id, related_pokemon.environment_id, current_pokemon.environment_id
HAVING related_pokemon.environment_id = current_pokemon.environment_id
OR COUNT(current_favourites.favorite_thing_id) > 0
)
@@ -5981,7 +5974,7 @@ export async function getPokemon(id: number, locale = defaultLocale) {
FROM scored_pokemon
JOIN pokemon related_pokemon ON related_pokemon.id = scored_pokemon.id
JOIN environments related_environment ON related_environment.id = related_pokemon.environment_id
ORDER BY scored_pokemon."environmentMatches" DESC, scored_pokemon."favoriteThingMatchCount" DESC, scored_pokemon.sort_order, related_pokemon.id
ORDER BY scored_pokemon."environmentMatches" DESC, scored_pokemon."favoriteThingMatchCount" DESC, related_pokemon.id
`,
[id]
),
@@ -6369,10 +6362,10 @@ export async function listHabitats(paramsQuery: QueryParams = {}, locale = defau
'name', pokemon_rows.name,
'isEventItem', pokemon_rows.is_event_item
)
ORDER BY pokemon_rows.sort_order, pokemon_rows.id
ORDER BY pokemon_rows.id
)
FROM (
SELECT DISTINCT p.id, p.display_id, ${pokemonName} AS name, p.is_event_item, p.sort_order
SELECT DISTINCT p.id, p.display_id, ${pokemonName} AS name, p.is_event_item
FROM habitat_pokemon hp
JOIN pokemon p ON p.id = hp.pokemon_id
WHERE hp.habitat_id = h.id
@@ -6443,7 +6436,7 @@ export async function getHabitat(id: number, locale = defaultLocale) {
JOIN pokemon p ON p.id = hp.pokemon_id
JOIN maps m ON m.id = hp.map_id
WHERE hp.habitat_id = $1
ORDER BY hp.rarity, ${orderByEntity('p')}, ${orderByEntity('m')}
ORDER BY hp.rarity, p.id, ${orderByEntity('m')}
`,
[id]
),
@@ -6855,7 +6848,7 @@ export async function getItem(id: number, locale = defaultLocale) {
JOIN skills s ON s.id = psid.skill_id
WHERE psid.item_id = $1
AND s.has_item_drop = true
ORDER BY ${orderByEntity('p')}, ${orderByEntity('s')}
ORDER BY p.id, ${orderByEntity('s')}
`,
[id]
),
@@ -6893,7 +6886,7 @@ export async function getItem(id: number, locale = defaultLocale) {
WHERE ps.pokemon_id = p.id
AND trading_skill.has_trading = true
)
ORDER BY pti.preference DESC, ${orderByEntity('p')}
ORDER BY pti.preference DESC, p.id
`,
[id]
),
@@ -8493,7 +8486,7 @@ async function exportGenericScopeData(client: DbClient, entityType: string, incl
async function exportScopeData(client: DbClient, scope: DataToolScope): Promise<DataToolScopeData> {
if (scope === 'pokemon') {
return {
pokemon: await tableRows(client, 'SELECT * FROM pokemon ORDER BY sort_order, id'),
pokemon: await tableRows(client, 'SELECT * FROM pokemon ORDER BY id'),
pokemonTypeLinks: await tableRows(client, 'SELECT * FROM pokemon_pokemon_types ORDER BY pokemon_id, slot_order'),
pokemonSkills: await tableRows(client, 'SELECT * FROM pokemon_skills ORDER BY pokemon_id, skill_id'),
pokemonFavoriteThings: await tableRows(client, 'SELECT * FROM pokemon_favorite_things ORDER BY pokemon_id, favorite_thing_id'),

View File

@@ -111,7 +111,6 @@ import {
reorderHabitats,
reorderItems,
reorderLanguages,
reorderPokemon,
reorderRecipes,
retryEntityDiscussionCommentModeration,
retryLifeCommentModeration,
@@ -2092,11 +2091,6 @@ app.delete('/api/admin/daily-checklist/:id', async (request, reply) => {
return deleted ? reply.code(204).send() : notFound(reply, request);
});
app.put('/api/admin/pokemon/order', async (request, reply) => {
const user = await requirePermissionWithRateLimits(request, reply, 'pokemon.order', 'wikiWrite');
return user ? reorderPokemon(request.body as Record<string, unknown>, user.id, requestLocale(request)) : undefined;
});
app.put('/api/admin/items/order', async (request, reply) => {
const user = await requirePermissionWithRateLimits(request, reply, 'items.order', 'wikiWrite');
return user ? reorderItems(request.body as Record<string, unknown>, user.id, requestLocale(request)) : undefined;