feat: add ancient artifacts and refactor item categories
Introduce Ancient Artifacts with full CRUD and image support Migrate item categories and usages to system-defined lists Add display_id to items and artifacts for custom sorting
This commit is contained in:
@@ -30,10 +30,12 @@ CREATE TABLE IF NOT EXISTS entity_translations (
|
||||
'item-usages',
|
||||
'acquisition-methods',
|
||||
'items',
|
||||
'ancient-artifacts',
|
||||
'maps',
|
||||
'habitats',
|
||||
'daily-checklist-items',
|
||||
'life-tags'
|
||||
'life-tags',
|
||||
'game-versions'
|
||||
)
|
||||
),
|
||||
entity_id integer NOT NULL,
|
||||
@@ -46,6 +48,30 @@ CREATE TABLE IF NOT EXISTS entity_translations (
|
||||
CREATE INDEX IF NOT EXISTS entity_translations_lookup_idx
|
||||
ON entity_translations (entity_type, entity_id, field_name, locale);
|
||||
|
||||
ALTER TABLE entity_translations
|
||||
DROP CONSTRAINT IF EXISTS entity_translations_entity_type_check;
|
||||
|
||||
ALTER TABLE entity_translations
|
||||
ADD CONSTRAINT entity_translations_entity_type_check CHECK (
|
||||
entity_type IN (
|
||||
'pokemon',
|
||||
'pokemon-types',
|
||||
'skills',
|
||||
'environments',
|
||||
'favorite-things',
|
||||
'item-categories',
|
||||
'item-usages',
|
||||
'acquisition-methods',
|
||||
'items',
|
||||
'ancient-artifacts',
|
||||
'maps',
|
||||
'habitats',
|
||||
'daily-checklist-items',
|
||||
'life-tags',
|
||||
'game-versions'
|
||||
)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS users (
|
||||
id integer GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
||||
email text NOT NULL UNIQUE,
|
||||
@@ -241,6 +267,11 @@ VALUES
|
||||
('items.delete', 'Delete items', 'Delete item records.', 'Items', true),
|
||||
('items.order', 'Order items', 'Reorder item records.', 'Items', true),
|
||||
('items.upload', 'Upload item images', 'Upload item images.', 'Items', true),
|
||||
('ancient-artifacts.create', 'Create Ancient Artifacts', 'Create Ancient Artifact records.', 'Ancient Artifacts', true),
|
||||
('ancient-artifacts.update', 'Update Ancient Artifacts', 'Edit Ancient Artifact records.', 'Ancient Artifacts', true),
|
||||
('ancient-artifacts.delete', 'Delete Ancient Artifacts', 'Delete Ancient Artifact records.', 'Ancient Artifacts', true),
|
||||
('ancient-artifacts.order', 'Order Ancient Artifacts', 'Reorder Ancient Artifact records.', 'Ancient Artifacts', true),
|
||||
('ancient-artifacts.upload', 'Upload Ancient Artifact images', 'Upload Ancient Artifact images.', 'Ancient Artifacts', true),
|
||||
('recipes.create', 'Create recipes', 'Create recipe records.', 'Recipes', true),
|
||||
('recipes.update', 'Update recipes', 'Edit recipe records.', 'Recipes', true),
|
||||
('recipes.delete', 'Delete recipes', 'Delete recipe records.', 'Recipes', true),
|
||||
@@ -327,6 +358,11 @@ JOIN permissions p ON p.key = ANY (ARRAY[
|
||||
'items.delete',
|
||||
'items.order',
|
||||
'items.upload',
|
||||
'ancient-artifacts.create',
|
||||
'ancient-artifacts.update',
|
||||
'ancient-artifacts.delete',
|
||||
'ancient-artifacts.order',
|
||||
'ancient-artifacts.upload',
|
||||
'recipes.create',
|
||||
'recipes.update',
|
||||
'recipes.delete',
|
||||
@@ -395,6 +431,10 @@ JOIN permissions p ON p.key = ANY (ARRAY[
|
||||
'items.update',
|
||||
'items.order',
|
||||
'items.upload',
|
||||
'ancient-artifacts.create',
|
||||
'ancient-artifacts.update',
|
||||
'ancient-artifacts.order',
|
||||
'ancient-artifacts.upload',
|
||||
'recipes.create',
|
||||
'recipes.update',
|
||||
'recipes.order',
|
||||
@@ -416,6 +456,31 @@ WHERE r.key = 'editor'
|
||||
)
|
||||
ON CONFLICT DO NOTHING;
|
||||
|
||||
INSERT INTO role_permissions (role_id, permission_id)
|
||||
SELECT r.id, p.id
|
||||
FROM roles r
|
||||
JOIN permissions p ON p.key = ANY (ARRAY[
|
||||
'ancient-artifacts.create',
|
||||
'ancient-artifacts.update',
|
||||
'ancient-artifacts.delete',
|
||||
'ancient-artifacts.order',
|
||||
'ancient-artifacts.upload'
|
||||
])
|
||||
WHERE r.key = 'admin'
|
||||
ON CONFLICT DO NOTHING;
|
||||
|
||||
INSERT INTO role_permissions (role_id, permission_id)
|
||||
SELECT r.id, p.id
|
||||
FROM roles r
|
||||
JOIN permissions p ON p.key = ANY (ARRAY[
|
||||
'ancient-artifacts.create',
|
||||
'ancient-artifacts.update',
|
||||
'ancient-artifacts.order',
|
||||
'ancient-artifacts.upload'
|
||||
])
|
||||
WHERE r.key = 'editor'
|
||||
ON CONFLICT DO NOTHING;
|
||||
|
||||
INSERT INTO role_permissions (role_id, permission_id)
|
||||
SELECT r.id, p.id
|
||||
FROM roles r
|
||||
@@ -798,8 +863,12 @@ CREATE TABLE IF NOT EXISTS acquisition_methods (
|
||||
|
||||
CREATE TABLE IF NOT EXISTS items (
|
||||
id integer GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
||||
display_id integer NOT NULL CHECK (display_id > 0),
|
||||
name text NOT NULL UNIQUE,
|
||||
category_id integer NOT NULL REFERENCES item_categories(id),
|
||||
details text NOT NULL DEFAULT '',
|
||||
category_key text NOT NULL DEFAULT 'other',
|
||||
usage_key text,
|
||||
category_id integer REFERENCES item_categories(id),
|
||||
usage_id integer REFERENCES item_usages(id),
|
||||
dyeable boolean NOT NULL DEFAULT false,
|
||||
dual_dyeable boolean NOT NULL DEFAULT false,
|
||||
@@ -811,6 +880,35 @@ CREATE TABLE IF NOT EXISTS items (
|
||||
created_by_user_id integer REFERENCES users(id) ON DELETE SET NULL,
|
||||
updated_by_user_id integer REFERENCES users(id) ON DELETE SET NULL,
|
||||
created_at timestamptz NOT NULL DEFAULT now(),
|
||||
updated_at timestamptz NOT NULL DEFAULT now(),
|
||||
CHECK (category_key IN (
|
||||
'furniture',
|
||||
'misc',
|
||||
'outdoor',
|
||||
'utilities',
|
||||
'buildings',
|
||||
'blocks',
|
||||
'kits',
|
||||
'nature',
|
||||
'food',
|
||||
'materials',
|
||||
'key-items',
|
||||
'other'
|
||||
)),
|
||||
CHECK (usage_key IS NULL OR usage_key IN ('decoration', 'relaxation', 'toy', 'road'))
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS ancient_artifacts (
|
||||
id integer GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
||||
display_id integer NOT NULL UNIQUE CHECK (display_id > 0),
|
||||
name text NOT NULL UNIQUE,
|
||||
details text NOT NULL DEFAULT '',
|
||||
category_key text NOT NULL CHECK (category_key IN ('lost-relics-l', 'lost-relics-s', 'fossils')),
|
||||
image_path text NOT NULL DEFAULT '',
|
||||
sort_order integer NOT NULL DEFAULT 0 CHECK (sort_order >= 0),
|
||||
created_by_user_id integer REFERENCES users(id) ON DELETE SET NULL,
|
||||
updated_by_user_id integer REFERENCES users(id) ON DELETE SET NULL,
|
||||
created_at timestamptz NOT NULL DEFAULT now(),
|
||||
updated_at timestamptz NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
@@ -842,6 +940,12 @@ CREATE TABLE IF NOT EXISTS item_favorite_things (
|
||||
PRIMARY KEY (item_id, favorite_thing_id)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS ancient_artifact_favorite_things (
|
||||
ancient_artifact_id integer NOT NULL REFERENCES ancient_artifacts(id) ON DELETE CASCADE,
|
||||
favorite_thing_id integer NOT NULL REFERENCES favorite_things(id),
|
||||
PRIMARY KEY (ancient_artifact_id, favorite_thing_id)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS pokemon_skill_item_drops (
|
||||
pokemon_id integer NOT NULL,
|
||||
skill_id integer NOT NULL,
|
||||
@@ -899,6 +1003,116 @@ CREATE TABLE IF NOT EXISTS habitat_pokemon (
|
||||
ALTER TABLE life_tags
|
||||
ADD COLUMN IF NOT EXISTS is_default boolean NOT NULL DEFAULT false;
|
||||
|
||||
ALTER TABLE items
|
||||
ADD COLUMN IF NOT EXISTS display_id integer,
|
||||
ADD COLUMN IF NOT EXISTS details text NOT NULL DEFAULT '',
|
||||
ADD COLUMN IF NOT EXISTS category_key text,
|
||||
ADD COLUMN IF NOT EXISTS usage_key text;
|
||||
|
||||
ALTER TABLE ancient_artifacts
|
||||
ADD COLUMN IF NOT EXISTS image_path text NOT NULL DEFAULT '';
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
IF EXISTS (
|
||||
SELECT 1
|
||||
FROM information_schema.columns
|
||||
WHERE table_name = 'items'
|
||||
AND column_name = 'category_id'
|
||||
AND table_schema = current_schema()
|
||||
) THEN
|
||||
ALTER TABLE items ALTER COLUMN category_id DROP NOT NULL;
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
UPDATE items
|
||||
SET display_id = id
|
||||
WHERE display_id IS NULL;
|
||||
|
||||
UPDATE items i
|
||||
SET category_key = CASE lower(trim(c.name))
|
||||
WHEN 'furniture' THEN 'furniture'
|
||||
WHEN 'misc' THEN 'misc'
|
||||
WHEN 'outdoor' THEN 'outdoor'
|
||||
WHEN 'utilities' THEN 'utilities'
|
||||
WHEN 'buildings' THEN 'buildings'
|
||||
WHEN 'blocks' THEN 'blocks'
|
||||
WHEN 'kits' THEN 'kits'
|
||||
WHEN 'nature' THEN 'nature'
|
||||
WHEN 'food' THEN 'food'
|
||||
WHEN 'materials' THEN 'materials'
|
||||
WHEN 'key items' THEN 'key-items'
|
||||
WHEN 'key-items' THEN 'key-items'
|
||||
WHEN 'other' THEN 'other'
|
||||
ELSE 'other'
|
||||
END
|
||||
FROM item_categories c
|
||||
WHERE i.category_id = c.id
|
||||
AND (i.category_key IS NULL OR i.category_key = '');
|
||||
|
||||
UPDATE items i
|
||||
SET usage_key = CASE lower(trim(u.name))
|
||||
WHEN 'decoration' THEN 'decoration'
|
||||
WHEN 'relaxation' THEN 'relaxation'
|
||||
WHEN 'toy' THEN 'toy'
|
||||
WHEN 'road' THEN 'road'
|
||||
ELSE NULL
|
||||
END
|
||||
FROM item_usages u
|
||||
WHERE i.usage_id = u.id
|
||||
AND i.usage_key IS NULL;
|
||||
|
||||
UPDATE items
|
||||
SET category_key = 'other'
|
||||
WHERE category_key IS NULL
|
||||
OR category_key NOT IN (
|
||||
'furniture',
|
||||
'misc',
|
||||
'outdoor',
|
||||
'utilities',
|
||||
'buildings',
|
||||
'blocks',
|
||||
'kits',
|
||||
'nature',
|
||||
'food',
|
||||
'materials',
|
||||
'key-items',
|
||||
'other'
|
||||
);
|
||||
|
||||
UPDATE items
|
||||
SET usage_key = NULL
|
||||
WHERE usage_key IS NOT NULL
|
||||
AND usage_key NOT IN ('decoration', 'relaxation', 'toy', 'road');
|
||||
|
||||
ALTER TABLE items
|
||||
ALTER COLUMN display_id SET NOT NULL,
|
||||
ALTER COLUMN category_key SET NOT NULL,
|
||||
ALTER COLUMN category_key SET DEFAULT 'other';
|
||||
|
||||
ALTER TABLE items
|
||||
DROP CONSTRAINT IF EXISTS items_display_id_positive,
|
||||
DROP CONSTRAINT IF EXISTS items_category_key_check,
|
||||
DROP CONSTRAINT IF EXISTS items_usage_key_check;
|
||||
|
||||
ALTER TABLE items
|
||||
ADD CONSTRAINT items_display_id_positive CHECK (display_id > 0),
|
||||
ADD CONSTRAINT items_category_key_check CHECK (category_key IN (
|
||||
'furniture',
|
||||
'misc',
|
||||
'outdoor',
|
||||
'utilities',
|
||||
'buildings',
|
||||
'blocks',
|
||||
'kits',
|
||||
'nature',
|
||||
'food',
|
||||
'materials',
|
||||
'key-items',
|
||||
'other'
|
||||
)),
|
||||
ADD CONSTRAINT items_usage_key_check CHECK (usage_key IS NULL OR usage_key IN ('decoration', 'relaxation', 'toy', 'road'));
|
||||
|
||||
CREATE INDEX IF NOT EXISTS environments_sort_order_idx ON environments(sort_order, id);
|
||||
CREATE INDEX IF NOT EXISTS skills_sort_order_idx ON skills(sort_order, id);
|
||||
CREATE INDEX IF NOT EXISTS favorite_things_sort_order_idx ON favorite_things(sort_order, id);
|
||||
@@ -911,6 +1125,10 @@ CREATE INDEX IF NOT EXISTS item_categories_sort_order_idx ON item_categories(sor
|
||||
CREATE INDEX IF NOT EXISTS item_usages_sort_order_idx ON item_usages(sort_order, id);
|
||||
CREATE INDEX IF NOT EXISTS acquisition_methods_sort_order_idx ON acquisition_methods(sort_order, id);
|
||||
CREATE INDEX IF NOT EXISTS items_sort_order_idx ON items(sort_order, id);
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS items_display_event_item_key ON items(display_id, is_event_item);
|
||||
CREATE INDEX IF NOT EXISTS items_display_order_idx ON items(is_event_item, display_id, sort_order, id);
|
||||
CREATE INDEX IF NOT EXISTS ancient_artifacts_sort_order_idx ON ancient_artifacts(sort_order, id);
|
||||
CREATE INDEX IF NOT EXISTS ancient_artifacts_display_order_idx ON ancient_artifacts(display_id, sort_order, id);
|
||||
CREATE INDEX IF NOT EXISTS recipes_sort_order_idx ON recipes(sort_order, id);
|
||||
CREATE INDEX IF NOT EXISTS maps_sort_order_idx ON maps(sort_order, id);
|
||||
CREATE INDEX IF NOT EXISTS habitats_sort_order_idx ON habitats(sort_order, id);
|
||||
@@ -933,7 +1151,7 @@ CREATE INDEX IF NOT EXISTS wiki_edit_logs_user_id_idx
|
||||
|
||||
CREATE TABLE IF NOT EXISTS entity_image_uploads (
|
||||
id integer GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
||||
entity_type text NOT NULL CHECK (entity_type IN ('pokemon', 'items', 'habitats')),
|
||||
entity_type text NOT NULL CHECK (entity_type IN ('pokemon', 'items', 'habitats', 'ancient-artifacts')),
|
||||
entity_id integer,
|
||||
entity_name text NOT NULL,
|
||||
path text NOT NULL UNIQUE,
|
||||
@@ -946,6 +1164,14 @@ CREATE TABLE IF NOT EXISTS entity_image_uploads (
|
||||
CHECK (path !~ '(^/|\\.\\.)')
|
||||
);
|
||||
|
||||
ALTER TABLE entity_image_uploads
|
||||
DROP CONSTRAINT IF EXISTS entity_image_uploads_entity_type_check;
|
||||
|
||||
ALTER TABLE entity_image_uploads
|
||||
ADD CONSTRAINT entity_image_uploads_entity_type_check CHECK (
|
||||
entity_type IN ('pokemon', 'items', 'habitats', 'ancient-artifacts')
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS entity_image_uploads_entity_idx
|
||||
ON entity_image_uploads(entity_type, entity_id, created_at DESC, id DESC);
|
||||
|
||||
@@ -954,7 +1180,7 @@ CREATE INDEX IF NOT EXISTS entity_image_uploads_user_idx
|
||||
|
||||
CREATE TABLE IF NOT EXISTS entity_discussion_comments (
|
||||
id integer GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
||||
entity_type text NOT NULL CHECK (entity_type IN ('pokemon', 'items', 'recipes', 'habitats')),
|
||||
entity_type text NOT NULL CHECK (entity_type IN ('pokemon', 'items', 'recipes', 'habitats', 'ancient-artifacts')),
|
||||
entity_id integer NOT NULL,
|
||||
parent_comment_id integer REFERENCES entity_discussion_comments(id) ON DELETE CASCADE,
|
||||
body text NOT NULL CHECK (length(body) BETWEEN 1 AND 1000),
|
||||
@@ -980,6 +1206,14 @@ CREATE INDEX IF NOT EXISTS entity_discussion_comments_parent_idx
|
||||
CREATE INDEX IF NOT EXISTS entity_discussion_comments_user_idx
|
||||
ON entity_discussion_comments(created_by_user_id);
|
||||
|
||||
ALTER TABLE entity_discussion_comments
|
||||
DROP CONSTRAINT IF EXISTS entity_discussion_comments_entity_type_check;
|
||||
|
||||
ALTER TABLE entity_discussion_comments
|
||||
ADD CONSTRAINT entity_discussion_comments_entity_type_check CHECK (
|
||||
entity_type IN ('pokemon', 'items', 'recipes', 'habitats', 'ancient-artifacts')
|
||||
);
|
||||
|
||||
ALTER TABLE life_tags
|
||||
ADD COLUMN IF NOT EXISTS is_rateable boolean NOT NULL DEFAULT false;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user