mirror of
https://github.com/supabase/supabase.git
synced 2026-10-10 03:45:06 +03:00
## What PR 2 of a stacked refactor of the SQL editor snippet state. **Stacked on #47203 (PR 1)** — review/merge that first. Extracts scattered business rules + the upsert-payload builder into a new **pure** module `apps/studio/state/sql-editor/sql-editor-rules.ts` (no Valtio, React, toast, or runtime data-layer imports): - `canEditSnippet` — read-only rule (shared snippet you don't own), was inline in `MonacoEditor` `disableEdit` - `isSnippetOwner` — owner check, was inline in `ReadOnlyBadge` / `SavingIndicator` - `validateMoveToFolder` — 'shared snippet cannot be within a folder', was a buried `toast.error` - `buildUpsertPayload` — the PUT /content payload, was an inline object literal (all `??` defaults preserved) - `isLoadedSnippet` — type guard (see below) ## Bug fix: no more empty-content saves (and no non-null assertion) The old payload builder used `{ ...content!, content_id: id }`. Tracing that `!` upstream surfaced a real bug: **favoriting a snippet from the sidebar that had never been opened** enqueued a save with no loaded content, producing a PUT with an empty content body (rejected by API). The requirement that a persisted snippet has loaded content is now enforced **at the type level** rather than by a runtime assertion or comment: - `buildUpsertPayload` accepts only a `LoadedSnippet` (content non-nullable) — the `!` is gone. - the save subscriber crosses that boundary via the `isLoadedSnippet` type guard. - the sidebar favorite toggle loads content first (mirroring `onSelectDuplicate` / the share modals), narrowing the fetched union content to the SQL variant via its discriminant — **no type cast**. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Improved consistency in read-only behavior and ownership checks across the SQL editor by centralizing permission logic. * Fixed favorite toggle to ensure snippet content is fully loaded before persisting changes. * **Refactor** * Centralized SQL snippet permission rules and validation logic into a dedicated helper module. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
Writing components
Where to create your components
- For components that declare the general structure and layout of a page:
/components/layouts/xxx
- For components that are tightly coupled to a specific interface:
/components/interfaces/xxx
- For components that are meant to be reusable across multiple pages:
/components/ui/xxx
- Note: We're gradually moving files out of the
to-be-cleanedfolder into the respective folders as we refactor
Component structure
- If a component has constants and utility methods that are tightly coupled to itself, keep them close to the component and enclose them in a folder with an
index.tsxas an entry point - Otherwise it can just be a file on its own
- For example:
-
components/ui - SampleComponentA - SampleComponentA.tsx - SampleComponentA.constants.ts - SampleComponentA.utils.ts - SampleComponentA.types.ts - index.ts - SampleComponentB.tsx
-
Template for building components
// Declare the prop types of your component
interface ComponentAProps {
sampleProp: string
}
// Name your component accordingly
const ComponentA = ({ sampleProp }: ComponentAProps) => {
return <div>ComponentA: {sampleProp}</div>
}
export default ComponentA