Repository navigation
Use Supabase views for review queues and split reviews page into new vs. existing edits - #45
Open
tayloraj10 wants to merge 11 commits into
Open
tayloraj10 wants to merge 11 commits into
tayloraj10 wants to merge 11 commits into
Conversation
Convert the "Save changes" button on the review detail page from a bare onClick handler to a <fetcher.Form onSubmit>, matching the approve/reject actions that already use <Form>. The payload is a nested object rather than flat fields, so it's still sent as JSON via fetcher.submit, but routing it through a real form gives us proper submit semantics (Enter-to-submit, pending state, progressive enhancement) instead of a plain button click. Also swap the deprecated FormEvent type for SubmitEvent, and convert logout to a POST action (was a GET loader) triggered via a <Form> in the sidebar, since a GET route shouldn't perform a mutation like signing out.
- Introduced new API methods for fetching edit counts and change logs, enhancing the resource edit management capabilities. - Updated the dashboard to utilize these new methods, allowing for a comprehensive view of pending edits and historical changes. - Modified the reviews detail page to display the change log for individual resources, improving audit trail visibility. - Enhanced the reviews index page to show pending edit counts for resources, aiding in resource management and review prioritization.
- Introduced separate types for edit and new row data, improving type safety and clarity. - Updated table columns to include a submitted date for both edits and new resources, enhancing the review process. - Refactored the ReviewQueueTable component to handle sorting and rendering more efficiently. - Improved the overall layout by incorporating a Stack component for better spacing and organization.
- Added a submitted date column to the reviews index, improving visibility of edit timelines. - Refactored the ReviewQueueTable component for better data handling and presentation. - Updated type definitions for edit and new row data to enhance clarity and type safety.
marcbachan
requested changes
Sep 8, 2026
The migration enforced at most one PENDING edit per resource by overwriting the existing row on each new submission. That makes the "Pending edits" column this PR adds permanently show 1 -- the warning chip for competing edits can never fire. It also destroys data: three people reporting three different things about one resource leaves only the last, unrecoverable and unseen by any reviewer. Returning NULL from the BEFORE INSERT trigger makes INSERT ... RETURNING yield zero rows, which breaks any submitting client that reads back the row it just created. And because the trigger is SECURITY DEFINER, an anonymous submitter can overwrite another person's pending edit, bypassing RLS. Competing edits are a real problem, but deleting the losing submissions hides it rather than resolving it, and the underlying lost-update bug survives the migration anyway (an older snapshot approved after a newer one still reverts it). Handling that belongs in approve_edit, tracked separately. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QuKg4c7DHw29iNM2BXn6B3
Top submitters bucketed every unattributed edit under "Unknown", which then dominated the leaderboard: on current data it reads "Unknown 9" and says nothing. Skip null submitted_by instead, so the existing empty state renders when there is genuinely nothing to attribute. creator is not a substitute -- it is a mirrored ResourceEntry field carried in the proposed snapshot, so it describes who created the resource (values are ingest sources like 'phlask_app'), not who submitted the edit. Renamed the loader field creator -> submitter, which is what it has actually held since this PR. totalEdits/pendingCount now come from count queries with head: true rather than counting fetched rows, and the loader comment no longer claims the change log avoids scanning resource_edits -- it is a plain view over that table, so it reads the same rows, just narrower columns. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QuKg4c7DHw29iNM2BXn6B3
getChangeLogForResource relied on the view's own ORDER BY surviving an outer filtered query, which Postgres does not guarantee -- the change history could render in arbitrary order. Order it explicitly. The review detail loader fetched the resource and the change log serially, and unlike the resource fetch the change log was not guarded, so a view error took down the whole review page. Both are supporting context for the diff, so run them together and let each fall back to empty. ResourceEditQueueRow now declares mapped_resource as non-null: the view filters on `is not null` and inner-joins resources, so the narrower type is the accurate one. That removes the unreachable `: 0` branch behind the competing-edits count. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QuKg4c7DHw29iNM2BXn6B3
Nothing was ever setting this column: submitting clients do not send it and the admin dashboard only reads edits, never creates them. Every row arrives with submitted_by = null, which is why the dashboard has no submitters to attribute and the detail page cannot say who proposed a change. A BEFORE INSERT trigger fills it from request.jwt.claims (email, then sub) when it arrives blank, so attribution starts working with no client change. A value the client sent is never overwritten, malformed claims are caught so they cannot block a submission, and the trigger returns NEW so INSERT ... RETURNING is unaffected. Anonymous submissions still land with submitted_by = null -- an anon JWT carries no identity to record -- so attributing those needs the submitting client to send something. Includes the matching rollback, per the supabase/rollback convention. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QuKg4c7DHw29iNM2BXn6B3
Addresses review feedback on #45. getList on the resource-edits API has had no callers since the dashboard moved to the count and queue queries, so remove it. The review tables now key rows by the edit's own id instead of TanStack's default array index, so row identity stays stable when the loader revalidates with different data. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
phlask-map changed food.organization_type from an array to a single value in January 2026, but the dashboard still typed it as an array and rendered it in an <Autocomplete multiple>. Every food edit submitted from the current map stores a string, so opening its review page threw "value.map is not a function". Organization type is now a single select, with a reader that still accepts the old array shape. food.tags, which the map's food form sets, is added to the type and carried through the form so saving an edit no longer drops it. The enums now match the map and the stored data: VESSEL replaces PITCHER (never present in the data), and HAS_FOUNTAIN and NONOPERATIONAL are added. The existing array rows in resources were normalized separately on 2026-10-13. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Use Supabase views for review queues and split reviews page into new vs. existing edits
Problem
The reviews and dashboard pages were pulling data by scanning the full
resource_editstable directly, rather than using the purpose-builtresource_edit_countsandresource_change_logviews. The reviews index page also lumped new site submissions and edits to existing resources into a single table, making it harder to review either group on its own.Solution
resource_edit_countsandresource_change_logviews via newgetEditCounts,getChangeLog, andgetChangeLogForResourceAPI methods, instead of scanningresource_editsdirectly.Reference
Closes #43. Partially addresses #35 (splits new-resource vs. existing-edit review queues; the duplicate-detection confidence rating from #35 is not part of this PR).
Screenshots
Checklist