feat(frontend): app splitpanes (#1248)

* feat(frontend): app splitpanes

* feat(frontend): app splitpanes vertical

* feat(frontend): support both splitpanes

* done

* done

* default select value

* container height

---------

Co-authored-by: Ruben Fiszel <ruben@rubenfiszel.com>
This commit is contained in:
Faton Ramadani
2023-03-02 01:30:07 +01:00
committed by GitHub
parent 38fb3450c8
commit f4d79ee263
16 changed files with 393 additions and 93 deletions
+1 -1
View File
@@ -213,7 +213,7 @@
<div class="my-10" />
<Button
color="light"
size="sm"
size="xs"
endIcon={{ icon: viewCliOptions ? faChevronUp : faChevronDown }}
on:click={() => (viewCliOptions = !viewCliOptions)}
>
@@ -52,14 +52,21 @@
<div class="flex flex-col w-full">
<div class="flex items-center w-full gap-1 px-1">
<span class={css?.limits?.class ?? ''} style={css?.limits?.style ?? ''}>
{min}
{+min}
</span>
<div class="grow" on:pointerdown|stopPropagation>
<RangeSlider bind:slider bind:values {step} min={min ?? 0} max={max ?? 1} range />
<RangeSlider
bind:slider
bind:values
{step}
min={!min ? 0 : +min}
max={!max ? 1 : +max}
range
/>
<!-- <RangeSlider {step} range min={min ?? 0} max={max ?? 1} bind:values /> -->
</div>
<span class={css?.limits?.class ?? ''} style={css?.limits?.style ?? ''}>
{max}
{+max}
</span>
</div>
<div class="flex justify-between px-1">
@@ -70,11 +70,21 @@
}
$: css = concatCustomCss($app.css?.selectcomponent, customCss)
let defaultValue: any = undefined
$: {
if (defaultValue) {
value = JSON.stringify(defaultValue)
outputs?.result.set(defaultValue)
}
}
</script>
<InputValue {id} input={configuration.items} bind:value={items} />
<InputValue {id} input={configuration.multiple} bind:value={multiple} />
<InputValue {id} input={configuration.placeholder} bind:value={placeholder} />
<InputValue {id} input={configuration.defaultValue} bind:value={defaultValue} />
<AlignWrapper {horizontalAlignment} {verticalAlignment}>
<div class="app-select w-full mx-0.5" style="height: 34px" on:pointerdown|stopPropagation>
@@ -58,13 +58,13 @@
<AlignWrapper {verticalAlignment}>
<div class="flex items-center w-full gap-1 px-1">
<span class={css?.limits?.class ?? ''} style={css?.limits?.style ?? ''}>
{min}
{+min}
</span>
<div class="grow" on:pointerdown|stopPropagation={() => ($selectedComponent = id)}>
<RangeSlider bind:slider bind:values {step} min={min ?? 0} max={max ?? 1} />
<RangeSlider bind:slider bind:values {step} min={+min} max={+max} />
</div>
<span class={css?.limits?.class ?? ''} style={css?.limits?.style ?? ''}>
{max}
{+max}
</span>
<span class="mx-2">
<span
@@ -16,8 +16,6 @@
export const staticOutputs: string[] = []
const { app, focusedGrid, selectedComponent } = getContext<AppEditorContext>('AppEditorContext')
let gridContent: string[] | undefined = undefined
function onFocus() {
$focusedGrid = {
parentComponentId: id,
@@ -31,18 +29,19 @@
</script>
<InputValue {id} input={configuration.noPadding} bind:value={noPadding} />
<InputValue {id} input={configuration.gridContent} bind:value={gridContent} />
{#if $app.subgrids?.[`${id}-0`]}
<SubGridEditor
{noPadding}
{id}
class={css?.container.class}
style={css?.container.style}
bind:subGrid={$app.subgrids[`${id}-0`]}
containerHeight={componentContainerHeight}
on:focus={() => {
$selectedComponent = id
}}
/>
{/if}
<div class="border">
{#if $app.subgrids?.[`${id}-0`]}
<SubGridEditor
{noPadding}
{id}
class={css?.container.class}
style={css?.container.style}
bind:subGrid={$app.subgrids[`${id}-0`]}
containerHeight={componentContainerHeight}
on:focus={() => {
$selectedComponent = id
}}
/>
{/if}
</div>
@@ -0,0 +1,66 @@
<script lang="ts">
import { getContext } from 'svelte'
import SubGridEditor from '../../editor/SubGridEditor.svelte'
import type { AppInput } from '../../inputType'
import type { AppEditorContext, ComponentCustomCSS } from '../../types'
import { concatCustomCss } from '../../utils'
import InputValue from '../helpers/InputValue.svelte'
import { Pane, Splitpanes } from 'svelte-splitpanes'
export let id: string
export let configuration: Record<string, AppInput>
export let componentContainerHeight: number
export let customCss: ComponentCustomCSS<'container'> | undefined = undefined
export let horizontal: boolean = false
export let panes: number[]
export const staticOutputs: string[] = []
const { app, focusedGrid, selectedComponent } = getContext<AppEditorContext>('AppEditorContext')
let noPadding: boolean | undefined = undefined
function onFocus() {
$focusedGrid = {
parentComponentId: id,
subGridIndex: 0
}
}
$: $selectedComponent === id && onFocus()
$: css = concatCustomCss($app.css?.containercomponent, customCss)
$: sumedup = panes.map((x) => (x / panes.reduce((a, b) => a + b, 0)) * 100)
</script>
<InputValue {id} input={configuration.noPadding} bind:value={noPadding} />
<div class="h-full w-full border" on:pointerdown|stopPropagation>
<Splitpanes {horizontal}>
{#each sumedup as paneSize, index}
<Pane bind:size={paneSize}>
{#if $app.subgrids?.[`${id}-${index}`]}
<SubGridEditor
noYPadding
{noPadding}
{id}
shouldHighlight={$focusedGrid?.subGridIndex === index}
class={css?.container.class}
style={css?.container.style}
bind:subGrid={$app.subgrids[`${id}-${index}`]}
containerHeight={horizontal
? (componentContainerHeight * paneSize) / 100 - 1
: componentContainerHeight}
on:focus={() => {
$selectedComponent = id
$focusedGrid = {
parentComponentId: id,
subGridIndex: index
}
}}
/>
{/if}
</Pane>
{/each}
</Splitpanes>
</div>
@@ -164,6 +164,10 @@
<Splitpanes horizontal>
<Pane size={$connectingInput?.opened ? 100 : 70}>
<div
on:pointerdown={(e) => {
$selectedComponent = undefined
$focusedGrid = undefined
}}
class={twMerge(
'bg-gray-100 h-full w-full',
$appStore.css?.['app']?.['viewer']?.class
@@ -177,7 +181,7 @@
)}
>
{#if $appStore.grid}
<div class={width}>
<div on:pointerdown|stopPropagation class={width}>
<GridEditor {policy} />
</div>
@@ -145,7 +145,7 @@
$focusedGrid = undefined
}}
>
<div class={!$focusedGrid ? 'border-indigo-600 border-2 border-dashed' : ''}>
<div class={!$focusedGrid && $mode !== 'preview' ? 'border-gray-400 border border-dashed' : ''}>
<Grid
onTopId={$selectedComponent}
fillSpace={false}
@@ -13,16 +13,18 @@
export { classes as class }
export let style = ''
export let noPadding = false
export let noYPadding = false
export let subGrid: GridItem[] = []
export let visible: boolean = true
export let id: string
export let shouldHighlight: boolean = true
const dispatch = createEventDispatcher()
const { app, connectingInput, selectedComponent, focusedGrid } =
const { app, connectingInput, selectedComponent, focusedGrid, mode } =
getContext<AppEditorContext>('AppEditorContext')
$: highlight = id === $focusedGrid?.parentComponentId
$: highlight = id === $focusedGrid?.parentComponentId && shouldHighlight
let pointerdown = false
let onComponent: string | undefined = undefined
@@ -69,13 +71,20 @@
bind:this={container}
>
<div
class={twMerge('py-2 overflow-auto', classes ?? '', noPadding ? 'px-0' : 'px-2')}
class={twMerge(
'overflow-auto',
noYPadding ? '' : 'py-2',
classes ?? '',
noPadding ? 'px-0' : 'px-2'
)}
on:pointerdown|stopPropagation={onpointerdown}
on:pointerleave={onpointerup}
on:pointerup={onpointerup}
style="height: {containerHeight}px; {style ?? ''}"
>
<div class={highlight ? 'border-indigo-600 border-2 border-dashed' : ''}>
<div
class={highlight && $mode !== 'preview' ? 'border-gray-400 border border-dashed h-full' : ''}
>
<Grid
bind:items={subGrid}
let:dataItem
@@ -38,6 +38,7 @@
import type { AppComponent } from './components'
import AppAggridTable from '../../components/display/table/AppAggridTable.svelte'
import AppDrawer from '../../components/layout/AppDrawer.svelte'
import AppSplitpanes from '../../components/layout/AppSplitpanes.svelte'
import { deepEqual } from 'fast-equals'
export let component: AppComponent
@@ -176,6 +177,8 @@
{:else if component.type === 'textcomponent'}
<AppText
id={component.id}
verticalAlignment={component.verticalAlignment}
horizontalAlignment={component.horizontalAlignment}
configuration={component.configuration}
customCss={component.customCss}
bind:initializing
@@ -185,6 +188,8 @@
{:else if component.type === 'buttoncomponent'}
<AppButton
id={component.id}
verticalAlignment={component.verticalAlignment}
horizontalAlignment={component.horizontalAlignment}
configuration={component.configuration}
customCss={component.customCss}
componentInput={component.componentInput}
@@ -193,6 +198,8 @@
{:else if component.type === 'selectcomponent'}
<AppSelect
id={component.id}
verticalAlignment={component.verticalAlignment}
horizontalAlignment={component.horizontalAlignment}
configuration={component.configuration}
customCss={component.customCss}
bind:staticOutputs={$staticOutputs[component.id]}
@@ -200,6 +207,7 @@
{:else if component.type === 'formcomponent'}
<AppForm
id={component.id}
horizontalAlignment={component.horizontalAlignment}
configuration={component.configuration}
customCss={component.customCss}
componentInput={component.componentInput}
@@ -208,6 +216,8 @@
{:else if component.type === 'formbuttoncomponent'}
<AppFormButton
id={component.id}
verticalAlignment={component.verticalAlignment}
horizontalAlignment={component.horizontalAlignment}
configuration={component.configuration}
customCss={component.customCss}
componentInput={component.componentInput}
@@ -216,6 +226,8 @@
{:else if component.type === 'checkboxcomponent'}
<AppCheckbox
id={component.id}
verticalAlignment={component.verticalAlignment}
horizontalAlignment={component.horizontalAlignment}
configuration={component.configuration}
customCss={component.customCss}
bind:staticOutputs={$staticOutputs[component.id]}
@@ -223,12 +235,14 @@
{:else if component.type === 'textinputcomponent'}
<AppTextInput
id={component.id}
verticalAlignment={component.verticalAlignment}
configuration={component.configuration}
customCss={component.customCss}
bind:staticOutputs={$staticOutputs[component.id]}
/>
{:else if component.type === 'passwordinputcomponent'}
<AppTextInput
verticalAlignment={component.verticalAlignment}
configuration={component.configuration}
inputType="password"
appCssKey="passwordinputcomponent"
@@ -238,6 +252,7 @@
/>
{:else if component.type === 'dateinputcomponent'}
<AppDateInput
verticalAlignment={component.verticalAlignment}
configuration={component.configuration}
inputType="date"
id={component.id}
@@ -246,6 +261,7 @@
/>
{:else if component.type === 'numberinputcomponent'}
<AppNumberInput
verticalAlignment={component.verticalAlignment}
configuration={component.configuration}
id={component.id}
customCss={component.customCss}
@@ -253,6 +269,7 @@
/>
{:else if component.type === 'currencycomponent'}
<AppCurrencyInput
verticalAlignment={component.verticalAlignment}
configuration={component.configuration}
id={component.id}
customCss={component.customCss}
@@ -260,6 +277,7 @@
/>
{:else if component.type === 'slidercomponent'}
<AppSliderInputs
verticalAlignment={component.verticalAlignment}
configuration={component.configuration}
id={component.id}
customCss={component.customCss}
@@ -267,6 +285,8 @@
/>
{:else if component.type === 'horizontaldividercomponent'}
<AppDivider
verticalAlignment={component.verticalAlignment}
horizontalAlignment={component.horizontalAlignment}
configuration={component.configuration}
id={component.id}
customCss={component.customCss}
@@ -274,6 +294,8 @@
/>
{:else if component.type === 'verticaldividercomponent'}
<AppDivider
verticalAlignment={component.verticalAlignment}
horizontalAlignment={component.horizontalAlignment}
configuration={component.configuration}
id={component.id}
customCss={component.customCss}
@@ -281,6 +303,7 @@
/>
{:else if component.type === 'rangecomponent'}
<AppRangeInput
verticalAlignment={component.verticalAlignment}
configuration={component.configuration}
id={component.id}
customCss={component.customCss}
@@ -303,8 +326,29 @@
bind:staticOutputs={$staticOutputs[component.id]}
{componentContainerHeight}
/>
{:else if component.type === 'verticalsplitpanescomponent'}
<AppSplitpanes
configuration={component.configuration}
id={component.id}
customCss={component.customCss}
panes={component.panes}
bind:staticOutputs={$staticOutputs[component.id]}
{componentContainerHeight}
/>
{:else if component.type === 'horizontalsplitpanescomponent'}
<AppSplitpanes
configuration={component.configuration}
id={component.id}
customCss={component.customCss}
panes={component.panes}
bind:staticOutputs={$staticOutputs[component.id]}
{componentContainerHeight}
horizontal={true}
/>
{:else if component.type === 'iconcomponent'}
<AppIcon
verticalAlignment={component.verticalAlignment}
horizontalAlignment={component.horizontalAlignment}
configuration={component.configuration}
id={component.id}
customCss={component.customCss}
@@ -326,6 +370,8 @@
/>
{:else if component.type === 'drawercomponent'}
<AppDrawer
verticalAlignment={component.verticalAlignment}
horizontalAlignment={component.horizontalAlignment}
configuration={component.configuration}
id={component.id}
customCss={component.customCss}
@@ -28,7 +28,9 @@ import {
SeparatorVertical,
Paperclip,
Image,
SidebarClose
SidebarClose,
FlipHorizontal,
FlipVertical
} from 'lucide-svelte'
import type { BaseAppComponent } from '../../types'
@@ -80,6 +82,12 @@ export type TabsComponent = BaseComponent<'tabscomponent'> & {
}
export type ContainerComponent = BaseComponent<'containercomponent'>
export type DrawerComponent = BaseComponent<'drawercomponent'>
export type VerticalSplitPanesComponent = BaseComponent<'verticalsplitpanescomponent'> & {
panes: number[]
}
export type HorizontalSplitPanesComponent = BaseComponent<'horizontalsplitpanescomponent'> & {
panes: number[]
}
export type AppComponent = BaseAppComponent &
(
@@ -115,6 +123,8 @@ export type AppComponent = BaseAppComponent &
| ImageComponent
| AggridComponent
| DrawerComponent
| VerticalSplitPanesComponent
| HorizontalSplitPanesComponent
)
export type AppComponentDimensions = `${IntRange<
@@ -864,6 +874,11 @@ Hello \${ctx.username}
fieldType: 'text',
value: 'Select an item',
onlyStatic: true
},
defaultValue: {
type: 'static',
value: undefined,
fieldType: 'object'
}
},
customCss: {
@@ -1365,5 +1380,55 @@ Hello \${ctx.username}
card: false,
numberOfSubgrids: 1
}
},
verticalsplitpanescomponent: {
name: 'Vertical Split Panes',
icon: FlipHorizontal,
dims: '2:8-6:8',
data: {
softWrap: true,
id: '',
type: 'verticalsplitpanescomponent',
configuration: {
noPadding: {
type: 'static',
fieldType: 'boolean',
value: false,
onlyStatic: true
}
},
customCss: {
container: { class: '', style: '' }
} as const,
componentInput: undefined,
card: false,
panes: [50, 50],
numberOfSubgrids: 2
}
},
horizontalsplitpanescomponent: {
name: 'Horizontal Split Panes',
icon: FlipVertical,
dims: '2:8-6:8',
data: {
softWrap: true,
id: '',
type: 'horizontalsplitpanescomponent',
configuration: {
noPadding: {
type: 'static',
fieldType: 'boolean',
value: false,
onlyStatic: true
},
},
customCss: {
container: { class: '', style: '' }
} as const,
componentInput: undefined,
card: false,
panes: [50, 50],
numberOfSubgrids: 2
}
}
}
@@ -7,7 +7,9 @@ const layout: ComponentSet = {
'containercomponent',
'horizontaldividercomponent',
'verticaldividercomponent',
'drawercomponent'
'drawercomponent',
'verticalsplitpanescomponent',
'horizontalsplitpanescomponent'
]
} as const
@@ -9,6 +9,18 @@ export const staticValues = {
chartThemeOptions: ['theme1', 'theme2', 'theme3'],
textStyleOptions: ['Title', 'Subtitle', 'Body', 'Label', 'Caption'],
currencyOptions: ['USD', 'EUR', 'GBP', 'CAD', 'AUD', 'JPY', 'CNY', 'INR', 'BRL'],
localeOptions: ['en-US', 'en-GB', 'en-IE', 'de-DE', 'fr-FR', 'br-FR', 'ja-JP', 'pt-TL', 'fr-CA', 'en-CA'],
objectFitOptions: ['contain', 'cover', 'fill']
localeOptions: [
'en-US',
'en-GB',
'en-IE',
'de-DE',
'fr-FR',
'br-FR',
'ja-JP',
'pt-TL',
'fr-CA',
'en-CA'
],
objectFitOptions: ['contain', 'cover', 'fill'],
splitPanelOptions: ['2', '3', '4']
} as const
@@ -1,6 +1,6 @@
<script lang="ts">
import Button from '$lib/components/common/button/Button.svelte'
import { faCopy, faTrashAlt } from '@fortawesome/free-solid-svg-icons'
import { faChevronDown, faChevronUp, faCopy, faTrashAlt } from '@fortawesome/free-solid-svg-icons'
import { getContext } from 'svelte'
import type { AppEditorContext, GridItem } from '../../types'
import PanelSection from './common/PanelSection.svelte'
@@ -24,6 +24,8 @@
import { duplicateGridItem } from '../appUtils'
import { deleteGridItem } from '../appUtils'
import MoveToOtherGrid from './MoveToOtherGrid.svelte'
import GridPane from './GridPane.svelte'
import { slide } from 'svelte/transition'
export let component: AppComponent
export let rowColumns = false
@@ -67,6 +69,8 @@
onDelete?.()
}
let viewCssOptions = false
$: extraLib =
component?.componentInput?.type === 'template' && $worldStore
? buildExtraLib($worldStore?.outputsById ?? {}, component?.id, false)
@@ -163,9 +167,9 @@
{#if component.type === 'tabscomponent'}
<GridTab bind:tabs={component.tabs} {component} />
{/if}
{#if component.type === 'tablecomponent' && Array.isArray(component.actionButtons)}
{:else if component.type === 'verticalsplitpanescomponent' || component.type === 'horizontalsplitpanescomponent'}
<GridPane bind:panes={component.panes} {component} />
{:else if component.type === 'tablecomponent' && Array.isArray(component.actionButtons)}
<TableActions id={component.id} bind:components={component.actionButtons} />
{/if}
@@ -176,13 +180,27 @@
{#if Object.keys(component.customCss ?? {}).length > 0}
<PanelSection title="Custom CSS">
{#each Object.keys(component.customCss ?? {}) as name}
{#if component?.customCss?.[name]}
<div class="w-full mb-2">
<CssProperty {name} bind:value={component.customCss[name]} />
</div>
{/if}
{/each}
<div slot="action">
<Button
color="light"
size="xs"
endIcon={{ icon: viewCssOptions ? faChevronUp : faChevronDown }}
on:click={() => (viewCssOptions = !viewCssOptions)}
>
{viewCssOptions ? 'Hide' : 'Show'}
</Button>
</div>
{#if viewCssOptions}
<div transition:slide>
{#each Object.keys(component.customCss ?? {}) as name}
{#if component?.customCss?.[name]}
<div class="w-full mb-2">
<CssProperty {name} bind:value={component.customCss[name]} />
</div>
{/if}
{/each}
</div>
{/if}
</PanelSection>
{/if}
@@ -0,0 +1,85 @@
<script lang="ts">
import Button from '$lib/components/common/button/Button.svelte'
import { faPlus, faTrashAlt } from '@fortawesome/free-solid-svg-icons'
import { getContext } from 'svelte'
import type { AppEditorContext } from '../../types'
import { deleteGridItem } from '../appUtils'
import type { AppComponent } from '../component'
import PanelSection from './common/PanelSection.svelte'
export let panes: number[]
export let component: AppComponent
const { app, staticOutputs, runnableComponents } =
getContext<AppEditorContext>('AppEditorContext')
function addTab() {
const numberOfPanes = panes.length
panes = Array(panes.length + 1)
.fill(0)
.map((_) => Math.floor(100 / (panes.length + 1)))
if (!$app.subgrids) {
$app.subgrids = {}
}
$app.subgrids[`${component.id}-${numberOfPanes}`] = []
component.numberOfSubgrids = panes.length
}
function deleteSubgrid(index: number) {
let subgrid = `${component.id}-${index}`
for (const item of $app!.subgrids![subgrid]) {
const components = deleteGridItem($app, item.data, subgrid, false)
console.log(components)
for (const key in components) {
delete $staticOutputs[key]
delete $runnableComponents[key]
}
}
$staticOutputs = $staticOutputs
$runnableComponents = $runnableComponents
for (let i = index; i < panes.length - 1; i++) {
$app!.subgrids![`${component.id}-${i}`] = $app!.subgrids![`${component.id}-${i + 1}`]
}
panes.splice(index, 1)
panes = panes
component.numberOfSubgrids = panes.length
$app = $app
}
</script>
<PanelSection title={`panes ${panes.length > 0 ? `(${panes.length})` : ''}`}>
{#if panes.length == 0}
<span class="text-xs text-gray-500">No panes</span>
{/if}
<div class="flex gap-2 flex-col mt-2">
{#each panes as value, index (index)}
<div class="flex flex-row gap-2 items-center relative">
<input type="number" bind:value />
<div class="absolute top-1 right-1">
<Button
size="xs"
color="light"
variant="border"
on:click={() => {
deleteSubgrid(index)
}}
iconOnly
btnClasses="!text-red-500"
startIcon={{ icon: faTrashAlt }}
/>
</div>
</div>
{/each}
<Button
size="xs"
color="light"
variant="border"
startIcon={{ icon: faPlus }}
on:click={addTab}
iconOnly
/>
</div>
</PanelSection>
+26 -49
View File
@@ -1,59 +1,36 @@
DO
$do$
BEGIN
IF NOT EXISTS (
SELECT
FROM pg_catalog.pg_roles
WHERE rolname = 'windmill_user') THEN
CREATE ROLE windmill_user;
LOCK TABLE pg_catalog.pg_roles;
GRANT ALL
ON ALL TABLES IN SCHEMA public
TO windmill_user;
CREATE ROLE windmill_user;
GRANT ALL PRIVILEGES
ON ALL SEQUENCES IN SCHEMA public
TO windmill_user;
GRANT ALL
ON ALL TABLES IN SCHEMA public
TO windmill_user;
ALTER DEFAULT PRIVILEGES
IN SCHEMA public
GRANT ALL ON TABLES TO windmill_user;
GRANT ALL PRIVILEGES
ON ALL SEQUENCES IN SCHEMA public
TO windmill_user;
ALTER DEFAULT PRIVILEGES
IN SCHEMA public
GRANT ALL ON SEQUENCES TO windmill_user;
ALTER DEFAULT PRIVILEGES
IN SCHEMA public
GRANT ALL ON TABLES TO windmill_user;
ALTER DEFAULT PRIVILEGES
IN SCHEMA public
GRANT ALL ON SEQUENCES TO windmill_user;
CREATE ROLE windmill_admin WITH BYPASSRLS;
END IF;
END
$do$;
GRANT ALL
ON ALL TABLES IN SCHEMA public
TO windmill_admin;
DO
$do$
BEGIN
IF NOT EXISTS (
SELECT
FROM pg_catalog.pg_roles
WHERE rolname = 'windmill_admin') THEN
CREATE ROLE windmill_admin WITH BYPASSRLS;
GRANT ALL PRIVILEGES
ON ALL SEQUENCES IN SCHEMA public
TO windmill_admin;
GRANT ALL
ON ALL TABLES IN SCHEMA public
TO windmill_admin;
ALTER DEFAULT PRIVILEGES
IN SCHEMA public
GRANT ALL ON TABLES TO windmill_admin;
GRANT ALL PRIVILEGES
ON ALL SEQUENCES IN SCHEMA public
TO windmill_admin;
ALTER DEFAULT PRIVILEGES
IN SCHEMA public
GRANT ALL ON TABLES TO windmill_admin;
ALTER DEFAULT PRIVILEGES
IN SCHEMA public
GRANT ALL ON SEQUENCES TO windmill_admin;
END IF;
END
$do$;
ALTER DEFAULT PRIVILEGES
IN SCHEMA public
GRANT ALL ON SEQUENCES TO windmill_admin;