fix(#64): Address PR comments

This commit is contained in:
Josh Creek
2026-01-10 17:06:52 +00:00
parent 7c68d121ce
commit 68cc2a0712
7 changed files with 308 additions and 16 deletions
@@ -30,13 +30,18 @@ BEGIN
FROM auth.users;
-- Calculate current stats: count of completed pokedexes
-- A pokedex is completed when all its entries have caught = true
-- A pokedex is completed when all its expected entries have caught = true
-- Join catch_records to pokedex_entries_mapping to validate against explicit expected entries
SELECT COUNT(*) INTO v_completed_pokedexes
FROM (
SELECT "pokedexId"
FROM catch_records
GROUP BY "pokedexId"
HAVING COUNT(*) = COUNT(*) FILTER (WHERE caught = true)
SELECT cr."pokedexId"
FROM catch_records cr
INNER JOIN pokedex_entries_mapping pem
ON cr."pokedexId" = pem."pokedexId"
AND cr."pokedexEntryId" = pem."pokedexEntryId"
GROUP BY cr."pokedexId"
HAVING COUNT(*) = COUNT(*) FILTER (WHERE cr.caught = true)
AND COUNT(*) = (SELECT COUNT(*) FROM pokedex_entries_mapping WHERE "pokedexId" = cr."pokedexId")
) completed;
-- Update cache using UPSERT pattern
@@ -0,0 +1,46 @@
-- Add pokedex_entries_mapping table to explicitly define which entries belong to each pokedex
-- This fixes the stats functions ambiguity issue by providing an explicit definition of expected entries
-- Create pokedex_entries_mapping table
CREATE TABLE pokedex_entries_mapping (
id BIGSERIAL PRIMARY KEY,
"pokedexId" UUID NOT NULL REFERENCES pokedexes(id) ON DELETE CASCADE,
"pokedexEntryId" BIGINT NOT NULL REFERENCES pokedex_entries(id) ON DELETE CASCADE,
UNIQUE("pokedexId", "pokedexEntryId")
);
-- Create indexes for performance
CREATE INDEX idx_pokedex_entries_mapping_pokedex_id ON pokedex_entries_mapping("pokedexId");
CREATE INDEX idx_pokedex_entries_mapping_entry_id ON pokedex_entries_mapping("pokedexEntryId");
-- Enable RLS
ALTER TABLE pokedex_entries_mapping ENABLE ROW LEVEL SECURITY;
-- RLS Policies
-- Public read access (mapping is reference data)
CREATE POLICY "Anyone can view pokedex entries mapping"
ON pokedex_entries_mapping
FOR SELECT USING (true);
-- Users can only insert mappings for their own pokedexes
CREATE POLICY "Users can insert own pokedex mappings"
ON pokedex_entries_mapping
FOR INSERT WITH CHECK (
EXISTS (
SELECT 1 FROM pokedexes
WHERE pokedexes.id = pokedex_entries_mapping."pokedexId"
AND pokedexes."userId" = auth.uid()
)
);
-- Users can only delete mappings for their own pokedexes
CREATE POLICY "Users can delete own pokedex mappings"
ON pokedex_entries_mapping
FOR DELETE USING (
EXISTS (
SELECT 1 FROM pokedexes
WHERE pokedexes.id = pokedex_entries_mapping."pokedexId"
AND pokedexes."userId" = auth.uid()
)
);
@@ -0,0 +1,25 @@
-- Populate pokedex_entries_mapping for existing pokedexes
-- This is a one-time migration to backfill data for pokedexes created before the mapping table
-- Insert mappings for all existing pokedexes
-- Apply the same filtering logic that the application uses to determine expected entries
INSERT INTO pokedex_entries_mapping ("pokedexId", "pokedexEntryId")
SELECT
p.id AS "pokedexId",
pe.id AS "pokedexEntryId"
FROM pokedexes p
CROSS JOIN pokedex_entries pe
WHERE
-- Form filter: if isFormDex is false, only include base forms
-- Base forms have form IS NULL, except Unown which has no base form (use 'A')
(p."isFormDex" = true OR (pe.form IS NULL OR (pe.pokemon = 'Unown' AND pe.form = 'A')))
-- Game scope filter: if gameScope is specified, filter by game
AND (p."gameScope" IS NULL OR pe."gamesToCatchIn" @> ARRAY[p."gameScope"]::TEXT[])
-- Region filter: if gameScope is specified, filter by region
AND (p."gameScope" IS NULL OR pe."regionToCatchIn" = (
SELECT region FROM region_game_mappings WHERE game = p."gameScope" LIMIT 1
))
ON CONFLICT ("pokedexId", "pokedexEntryId") DO NOTHING;
-- Add comment to document this migration
COMMENT ON TABLE pokedex_entries_mapping IS 'Junction table defining which pokedex_entries belong to each pokedex. Used for accurate stats calculation.';