diff --git a/frontend/src/lib/components/raw_apps/RawAppTemplatePicker.svelte b/frontend/src/lib/components/raw_apps/RawAppTemplatePicker.svelte index 53b9d91e20..89edb723ba 100644 --- a/frontend/src/lib/components/raw_apps/RawAppTemplatePicker.svelte +++ b/frontend/src/lib/components/raw_apps/RawAppTemplatePicker.svelte @@ -74,7 +74,7 @@ // What the picked role can reach, which is not what the data table holds. const access = createDatatableAccessResource( () => selectedDatatable, - () => (showRolePicker ? effectiveRole : undefined), + () => accessRole, () => opWs ) const roles = createRolesResource( @@ -122,15 +122,22 @@ }) const availableDatatables = $derived(datatables.current) - // Read like the role list: only once it answers for the data table selected. - // At mount it is the initial value, and during a switch it is the previous - // table's — neither says anything about this one. + // The role the access question is asked as. Until the role list settles this + // is `undefined`, which the server reads as the data table's configured + // default — a different question, and one whose answer says nothing about what + // the role finally selected may create. + const accessRole = $derived(showRolePicker ? effectiveRole : undefined) + // Read like the role list: only once it answers for what is selected now. At + // mount it is the initial value, during a switch the previous data table's, + // and in between the same data table asked as another role. + const accessSettled = $derived( + access.current.datatable === selectedDatatable && access.current.role === accessRole + ) const loadedAccess = $derived( - access.current.datatable === selectedDatatable + accessSettled ? access.current - : { datatable: selectedDatatable, schemas: [], canCreateSchema: false } + : { datatable: selectedDatatable, role: accessRole, schemas: [], canCreateSchema: false } ) - const accessSettled = $derived(access.current.datatable === selectedDatatable) const availableSchemas = $derived(loadedAccess.schemas) const canCreateSchema = $derived(loadedAccess.canCreateSchema) @@ -512,7 +519,10 @@ variant="default" size="sm" on:click={() => start(false)} - disabled={!templates[selectedTemplateIndex] || newSchemaAlreadyExists || !rolesSettled} + disabled={!templates[selectedTemplateIndex] || + newSchemaAlreadyExists || + !rolesSettled || + !accessSettled} > {$copilotInfo.workspaceDisabled ? 'Start' : 'Start without AI'} @@ -521,6 +531,7 @@ variant="accent" on:click={() => start(true)} disabled={!rolesSettled || + !accessSettled || !templates[selectedTemplateIndex] || !initialPrompt.trim() || newSchemaAlreadyExists} diff --git a/frontend/src/lib/components/raw_apps/datatableUtils.svelte.ts b/frontend/src/lib/components/raw_apps/datatableUtils.svelte.ts index 8a9cb9574b..c411b2d707 100644 --- a/frontend/src/lib/components/raw_apps/datatableUtils.svelte.ts +++ b/frontend/src/lib/components/raw_apps/datatableUtils.svelte.ts @@ -90,15 +90,18 @@ export function createDatatableAccessResource( return resource( () => [getDatatable() ?? '', getRole() ?? '', getWorkspace() ?? ''] as const, async ([datatable, role, workspace]): Promise<{ - /** What this answers for: until it matches the selection, the schemas and - * the right to create one are the previous data table's, and at mount - * they are the initial value rather than an answer at all. */ + /** What this answers for — both halves of it. Until they match the + * selection, the schemas and the right to create one belong to another + * data table or to another role, and at mount they are the initial value + * rather than an answer at all. What a role may create in is exactly the + * question, so an answer computed as a different one settles nothing. */ datatable: string | undefined + role: string | undefined schemas: string[] canCreateSchema: boolean }> => { - if (!datatable || !workspace) - return { datatable: datatable || undefined, schemas: [], canCreateSchema: false } + const asked = { datatable: datatable || undefined, role: role || undefined } + if (!datatable || !workspace) return { ...asked, schemas: [], canCreateSchema: false } try { const tables = await WorkspaceService.listDataTableTables({ workspace, @@ -107,16 +110,16 @@ export function createDatatableAccessResource( }) const entry = tables.find((t) => t.datatable_name === datatable) return { - datatable, + ...asked, schemas: Object.keys(entry?.schemas ?? {}).sort(), canCreateSchema: !!entry?.can_create_schema } } catch (e) { console.error('Failed to load datatable access:', e) - return { datatable, schemas: [], canCreateSchema: false } + return { ...asked, schemas: [], canCreateSchema: false } } }, - { initialValue: { datatable: undefined, schemas: [], canCreateSchema: false } } + { initialValue: { datatable: undefined, role: undefined, schemas: [], canCreateSchema: false } } ) }