chore(datatables): don't mount the roles UI until the ACL editor lands

Enforcement ships first. The permissions drawer is what turns roles on, and the
catalog section is what creates them — both are only useful once there is a way
to grant a role the privileges it needs, which arrives with the ACL editor. Left
mounted they would offer a feature whose other half does not exist.

The two components are complete and reviewed; only their call sites here are
commented out, with a note pointing the follow-up PRs at them.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BjfMkJyKzodxkobqGZ6Lqb
This commit is contained in:
Diego Imbert
2026-09-09 17:19:30 +02:00
co-authored by Claude Opus 5
parent 397116926f
commit 1f661f5816
@@ -92,7 +92,8 @@
type GetSettingsResponse,
type TestDataTableConnectionResponse
} from '$lib/gen'
import { superadmin, workspaceStore } from '$lib/stores'
// `superadmin` gates the commented-out roles section at the bottom; restore it there.
import { workspaceStore } from '$lib/stores'
import { createAsyncConfirmationModal } from '../common/confirmationModal/asyncConfirmationModal.svelte'
import ConfirmationModal from '../common/confirmationModal/ConfirmationModal.svelte'
import { resource } from 'runed'
@@ -100,8 +101,10 @@
import { Popover } from '../meltComponents'
import ExploreAssetButton from '../ExploreAssetButton.svelte'
import DataTableMigrationsButton from './DataTableMigrationsButton.svelte'
import DataTablePermissionsButton from './DataTablePermissionsButton.svelte'
import DataTableRolesSection from './DataTableRolesSection.svelte'
// Both components are complete and reviewed; their call sites in this file are commented
// out until the ACL editor lands. Uncomment these with them.
// import DataTablePermissionsButton from './DataTablePermissionsButton.svelte'
// import DataTableRolesSection from './DataTableRolesSection.svelte'
import { deepEqual } from 'fast-equals'
import { clone } from '$lib/utils'
import SettingsFooter from './SettingsFooter.svelte'
@@ -398,64 +401,67 @@
<span>/</span>
<span class="font-mono">{dataTable.reference.datatable}</span>
<Tooltip>
This fork uses its parent's data table rather than a copy of it, so the database
and its roles are decided in that workspace.
This fork uses its parent's data table rather than a copy of it, so the database and
its roles are decided in that workspace.
</Tooltip>
</div>
{:else}
<div class="flex gap-2">
<div class="relative">
{#if dataTable.database.resource_type === 'instance'}
<Tooltip wrapperClass="absolute mt-[0.6rem] right-2 z-20" placement="bottom-start">
Use Windmill's PostgreSQL instance
</Tooltip>
{/if}
<Select
items={[
{ value: 'postgresql', label: 'PostgreSQL' },
{
value: 'instance',
label: 'Instance',
disabled: isCloudHosted(),
subtitle: $isCustomInstanceDbEnabled
? undefined
: isCloudHosted()
? 'Not available on cloud'
: 'Superadmin only'
}
]}
bind:value={
() => dataTable.database.resource_type,
(resource_type) => {
dataTable.database = {
resource_type,
resource_path:
resource_type === 'instance' ? defaultInstanceDbName() : undefined
<div class="flex gap-2">
<div class="relative">
{#if dataTable.database.resource_type === 'instance'}
<Tooltip
wrapperClass="absolute mt-[0.6rem] right-2 z-20"
placement="bottom-start"
>
Use Windmill's PostgreSQL instance
</Tooltip>
{/if}
<Select
items={[
{ value: 'postgresql', label: 'PostgreSQL' },
{
value: 'instance',
label: 'Instance',
disabled: isCloudHosted(),
subtitle: $isCustomInstanceDbEnabled
? undefined
: isCloudHosted()
? 'Not available on cloud'
: 'Superadmin only'
}
]}
bind:value={
() => dataTable.database.resource_type,
(resource_type) => {
dataTable.database = {
resource_type,
resource_path:
resource_type === 'instance' ? defaultInstanceDbName() : undefined
}
}
}
}
id="database-type-select"
class="w-28"
/>
</div>
<div class="flex items-center gap-1 w-80 relative">
{#if dataTable.database.resource_type !== 'instance'}
<ResourcePicker
class="flex-1"
bind:value={dataTable.database.resource_path}
resourceType={dataTable.database.resource_type}
id="database-type-select"
class="w-28"
/>
{:else}
<CustomInstanceDbSelect
class="flex-1"
{confirmationModal}
{customInstanceDbs}
bind:value={dataTable.database.resource_path}
tag="datatable"
/>
{/if}
</div>
<div class="flex items-center gap-1 w-80 relative">
{#if dataTable.database.resource_type !== 'instance'}
<ResourcePicker
class="flex-1"
bind:value={dataTable.database.resource_path}
resourceType={dataTable.database.resource_type}
/>
{:else}
<CustomInstanceDbSelect
class="flex-1"
{confirmationModal}
{customInstanceDbs}
bind:value={dataTable.database.resource_path}
tag="datatable"
/>
{/if}
</div>
</div>
</div>
{/if}
</Cell>
@@ -466,11 +472,21 @@
datatable={dataTable.name}
disabled={!!dirtyMap[dataTable.name]}
/>
<!-- Data table roles: not mounted yet. The enforcement ships first and this
drawer is what turns it on, so leaving it reachable would expose a half of the
feature whose other half (the ACL editor, which grants the privileges a role
actually needs) does not exist yet.
DataTablePermissionsButton.svelte is complete and reviewed — reuse it rather
than rewriting it, and uncomment this together with the roles section at the
bottom of this file and the two imports at the top.
<DataTablePermissionsButton
workspace={$workspaceStore ?? ''}
datatable={dataTable.name}
disabled={!!dirtyMap[dataTable.name]}
/>
-->
<Button
size="xs"
color="light"
@@ -597,11 +613,19 @@
{/if}
{/if}
<!-- The instance role catalog, superadmin-only. Not mounted for the same reason as the
permissions drawer above: creating roles is only useful once there is a way to grant them
privileges, which arrives with the ACL editor.
DataTableRolesSection.svelte is complete and reviewed — reuse it rather than rewriting it,
and uncomment this together with the permissions button above and the two imports at the top.
{#if $superadmin && !isCloudHosted()}
<div class="mt-8">
<DataTableRolesSection />
</div>
{/if}
-->
<SettingsFooter
class="mt-8"