Add the Admin review queue
Lets an Admin/Super-Admin review pending catalog submissions and approve (in place, same id) or reject (with a required reason) them, per organized-ideas.md §3/§9. Backend (supabase/migrations/20260910010000_admin_review_queue.sql): - Per-user pending-submission cap (10), enforced in catalog_submissions' insert policy rather than trusted to the client. - catalog_entity_usage_impact(entity_type, entity_id): a SECURITY DEFINER, admin-gated aggregate function answering "how many diagrams reference this, and a short sample" by scanning diagrams.data JSONB — never raw diagram content, and available to regular Admins even though they don't otherwise have diagram visibility (only Super Admins do, per §6). - catalog_submission_submitters(ids[]): same admin-gated pattern, batched, so the queue can show who submitted something without opening general profile browsing to regular Admins. - Follow-up migration: a rejected submission had no way out (the delete policy only allowed withdrawing 'pending') — extended to allow 'rejected' too, so a submitter can dismiss one they don't intend to revise. - 12 new pgTAP tests (38/38 total) covering the cap, both privileged functions (including the non-admin-gets-rejected case), and withdrawing pending vs. rejected submissions. Frontend: - authStore: minimal role awareness, replacing TopBar's local username fetch, used to gate the Review Queue UI. - AdminSubmissionRepository/SupabaseAdminSubmissionRepository + adminReviewStore: list all submissions, approve/reject, usage impact, submitter usernames. - AdminReviewModal: per-submission diff view (current vs. proposed, both row-shaped via the existing catalog<->row mappers), a duplicate-detection nudge (Levenshtein distance against existing public device names) for new device submissions, and an inline impact-check for edits to already-public entries before approving. - TopBar: role-gated "Review Queue" button with a pending-count badge; "My Submissions" gets an unseen-outcome badge (localStorage-tracked, like the existing hidden-template preference) so a submitter notices a decision without having to keep reopening the modal. - Deliberately deferred: the site-wide announcement banner (its own follow-up, per discussion) and the Admin/Super-Admin role-assignment UI (§9's later phase — becoming an Admin locally still means setting profiles.role via SQL/Studio). Verified: tsc -b and oxlint clean; supabase db reset + 38/38 pgTAP tests pass; confirmed the two new RPC functions are actually reachable through PostgREST (not just raw SQL) via a live curl call; manually tested submit -> review -> approve/reject -> (for rejected) dismiss end to end.
This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
import { create } from 'zustand'
|
||||
import { supabase } from '../data/supabaseClient'
|
||||
|
||||
export type UserRole = 'regular' | 'admin' | 'super_admin'
|
||||
|
||||
interface AuthStoreState {
|
||||
userId: string | null
|
||||
username: string | null
|
||||
role: UserRole | null
|
||||
isLoaded: boolean
|
||||
|
||||
/** One-time fetch of your own profile — nothing else in the app changes
|
||||
* your own username or role while you're signed in (role changes are an
|
||||
* Admin/Super-Admin-only action on someone else's account, see
|
||||
* organized-ideas.md §6, not yet built), so there's no need to keep this live. */
|
||||
load: () => Promise<void>
|
||||
}
|
||||
|
||||
export const useAuthStore = create<AuthStoreState>((set) => ({
|
||||
userId: null,
|
||||
username: null,
|
||||
role: null,
|
||||
isLoaded: false,
|
||||
|
||||
load: async () => {
|
||||
const {
|
||||
data: { user },
|
||||
} = await supabase.auth.getUser()
|
||||
if (!user) {
|
||||
set({ isLoaded: true })
|
||||
return
|
||||
}
|
||||
const { data } = await supabase.from('profiles').select('username, role').eq('id', user.id).single()
|
||||
set({
|
||||
userId: user.id,
|
||||
username: data?.username ?? null,
|
||||
role: (data?.role as UserRole | undefined) ?? null,
|
||||
isLoaded: true,
|
||||
})
|
||||
},
|
||||
}))
|
||||
Reference in New Issue
Block a user