mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-08-18 16:02:10 +00:00
fix(frontend): tidy the resource drawer around the Supabase entry point
Connect Supabase was a hand-styled anchor carrying Supabase's brand hex values rather than a Button, and it sat in a row whose other controls had settled on unifiedSize md. Making it a Button meant SupabaseIcon had to satisfy IconType, so it now takes `size` (deriving height/width from it) alongside the string props its other callers pass. The manual resource form spaced every field 32px apart and WhitelistIp added another 16px of its own, which read as a gap rather than a rhythm. One gap of 16px, with the form itself given a little more separation from the description above it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -11,12 +11,18 @@
|
||||
import Button from './common/button/Button.svelte'
|
||||
import { Loader2 } from 'lucide-svelte'
|
||||
import { untrack } from 'svelte'
|
||||
import { base } from '$lib/base'
|
||||
import GitHubAppIntegration from './GitHubAppIntegration.svelte'
|
||||
import BedrockCredentialsCheck from './BedrockCredentialsCheck.svelte'
|
||||
import { isCloudHosted } from '$lib/cloud'
|
||||
import ResourceGen from './copilot/ResourceGen.svelte'
|
||||
import SyncResourceTypes from './SyncResourceTypes.svelte'
|
||||
import Modal2 from './common/modal/Modal2.svelte'
|
||||
import SupabaseProjectStep, {
|
||||
type SupabasePick
|
||||
} from './workspaceSettings/SupabaseProjectStep.svelte'
|
||||
import { supabaseResourceValue } from './workspaceSettings/supabaseProvisioning'
|
||||
import { useSupabaseOauth } from './workspaceSettings/supabaseOauth.svelte'
|
||||
import { sendUserToast } from '$lib/toast'
|
||||
|
||||
interface Props {
|
||||
resourceType: string
|
||||
@@ -127,6 +133,51 @@
|
||||
let rawCodeEditor: { setCode: (code: string) => void } | undefined = $state(undefined)
|
||||
let textFileContent: string | undefined = $state(undefined)
|
||||
|
||||
let supabaseOpen = $state(false)
|
||||
let supaStep: ReturnType<typeof SupabaseProjectStep> | undefined = $state(undefined)
|
||||
let supaResult: SupabasePick | undefined = $state(undefined)
|
||||
|
||||
// Authorizing is not something to present a dialog about first: the button goes straight
|
||||
// to the popup, and the dialog opens on the way back, already holding the projects.
|
||||
const supaOauth = useSupabaseOauth()
|
||||
let awaitingSupabaseAuth = $state(false)
|
||||
|
||||
function connectSupabase() {
|
||||
if (supaOauth.authed) {
|
||||
supabaseOpen = true
|
||||
return
|
||||
}
|
||||
awaitingSupabaseAuth = true
|
||||
supaOauth.connect()
|
||||
}
|
||||
|
||||
$effect(() => {
|
||||
if (awaitingSupabaseAuth && supaOauth.authed) {
|
||||
awaitingSupabaseAuth = false
|
||||
supabaseOpen = true
|
||||
}
|
||||
})
|
||||
|
||||
// The resource is being edited here rather than created for us, so the project's password
|
||||
// goes straight into the form as a value. The user can link it to a secret variable with
|
||||
// the same affordance every other password field has.
|
||||
function applySupabasePick(pick: SupabasePick) {
|
||||
args = {
|
||||
...(args ?? {}),
|
||||
...supabaseResourceValue(pick.project, ''),
|
||||
password: pick.password
|
||||
}
|
||||
rawCode = JSON.stringify(args, null, 2)
|
||||
rawCodeEditor?.setCode(rawCode)
|
||||
supabaseOpen = false
|
||||
supaResult = undefined
|
||||
sendUserToast(`Filled in the connection for ${pick.project.name}`)
|
||||
}
|
||||
|
||||
$effect(() => {
|
||||
if (supaResult) applySupabasePick(supaResult)
|
||||
})
|
||||
|
||||
function parseTextFileContent() {
|
||||
args = {
|
||||
content: textFileContent
|
||||
@@ -172,7 +223,7 @@
|
||||
}}
|
||||
>
|
||||
{#snippet trigger()}
|
||||
<Button spacingSize="sm" size="xs" variant="default" nonCaptureEvent>
|
||||
<Button spacingSize="sm" size="xs" unifiedSize="md" variant="default" nonCaptureEvent>
|
||||
From connection string
|
||||
</Button>
|
||||
{/snippet}
|
||||
@@ -206,14 +257,15 @@
|
||||
</Popover>
|
||||
{/if}
|
||||
{#if resourceType == 'postgresql' && supabaseWizard}
|
||||
<a
|
||||
target="_blank"
|
||||
href="{base}/api/oauth/connect/supabase_wizard"
|
||||
class="border rounded-lg flex flex-row gap-2 items-center text-xs px-3 py-1.5 h-8 bg-[#F1F3F5] hover:bg-[#E6E8EB] dark:bg-[#1C1C1C] dark:hover:bg-black"
|
||||
<Button
|
||||
unifiedSize="md"
|
||||
variant="default"
|
||||
startIcon={{ icon: SupabaseIcon }}
|
||||
loading={awaitingSupabaseAuth}
|
||||
on:click={connectSupabase}
|
||||
>
|
||||
<SupabaseIcon height="16px" width="16px" />
|
||||
<div class="text-[#11181C] dark:text-[#EDEDED] font-semibold">Connect Supabase</div>
|
||||
</a>
|
||||
Connect Supabase
|
||||
</Button>
|
||||
{/if}
|
||||
<GitHubAppIntegration
|
||||
{resourceType}
|
||||
@@ -286,3 +338,37 @@
|
||||
bind:args
|
||||
/>
|
||||
{/if}
|
||||
|
||||
<Modal2
|
||||
bind:isOpen={supabaseOpen}
|
||||
target="#content"
|
||||
title="Connect Supabase"
|
||||
contentClasses="flex flex-col"
|
||||
fixedWidth="md"
|
||||
fixedHeight="md"
|
||||
>
|
||||
<div class="flex h-full flex-col gap-3">
|
||||
<div class="flex-1 flex flex-col gap-3 min-h-0">
|
||||
<SupabaseProjectStep
|
||||
bind:this={supaStep}
|
||||
bind:result={supaResult}
|
||||
defaultProjectName={`windmill-${$workspaceStore ?? 'workspace'}`}
|
||||
continueLabel="Use this project"
|
||||
/>
|
||||
</div>
|
||||
{#if supaStep}
|
||||
{@const action = supaStep.getAction()}
|
||||
<div class="flex justify-end pt-3">
|
||||
<Button
|
||||
size="sm"
|
||||
variant="accent"
|
||||
disabled={action.disabled}
|
||||
loading={action.busy}
|
||||
onClick={() => action.act?.()}
|
||||
>
|
||||
{action.label}
|
||||
</Button>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</Modal2>
|
||||
|
||||
@@ -990,7 +990,7 @@
|
||||
/>
|
||||
</div>
|
||||
{:else if step == 2 && manual}
|
||||
<div class="flex flex-col gap-8">
|
||||
<div class="flex flex-col gap-4">
|
||||
<Label label="Path">
|
||||
<Path
|
||||
bind:error={pathError}
|
||||
@@ -1086,18 +1086,22 @@
|
||||
Acquire the token automatically via client credentials instead
|
||||
</button>
|
||||
{/if}
|
||||
{#key resourceTypeInfo}
|
||||
<ApiConnectForm
|
||||
bind:linkedSecrets
|
||||
bind:description
|
||||
{linkedSecretCandidates}
|
||||
{resourceType}
|
||||
{resourceTypeInfo}
|
||||
bind:args
|
||||
bind:isValid
|
||||
onSynced={getResourceTypeInfo}
|
||||
/>
|
||||
{/key}
|
||||
<!-- The form is a section of its own, not just the next field: it needs more of a break
|
||||
from the description than the uniform gap gives. -->
|
||||
<div class="mt-2">
|
||||
{#key resourceTypeInfo}
|
||||
<ApiConnectForm
|
||||
bind:linkedSecrets
|
||||
bind:description
|
||||
{linkedSecretCandidates}
|
||||
{resourceType}
|
||||
{resourceTypeInfo}
|
||||
bind:args
|
||||
bind:isValid
|
||||
onSynced={getResourceTypeInfo}
|
||||
/>
|
||||
{/key}
|
||||
</div>
|
||||
</div>
|
||||
{:else if step == 2 && !manual}
|
||||
{#if manual == false && resourceType != ''}
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
</script>
|
||||
|
||||
{#if ips}
|
||||
<div class="mt-4"></div>
|
||||
<Alert size="xs" type="info" title="IPs to whitelist">
|
||||
<span class="text-primary">If necessary, the workers IPs to whitelist are:</span>
|
||||
{ips.join(', ')}
|
||||
|
||||
@@ -124,6 +124,7 @@
|
||||
<Button
|
||||
color={genLoading ? 'red' : 'light'}
|
||||
size="xs"
|
||||
unifiedSize="md"
|
||||
nonCaptureEvent={!genLoading}
|
||||
startIcon={{ icon: Wand2 }}
|
||||
iconOnly
|
||||
|
||||
@@ -1,24 +1,63 @@
|
||||
<script lang="ts">
|
||||
interface Props {
|
||||
height?: string;
|
||||
width?: string;
|
||||
height?: string
|
||||
width?: string
|
||||
/** Accepting `size` is what makes this usable as a Button `startIcon`. */
|
||||
size?: number
|
||||
class?: string
|
||||
}
|
||||
|
||||
let { height = '24px', width = '24px' }: Props = $props();
|
||||
let {
|
||||
size,
|
||||
height = size ? `${size}px` : '24px',
|
||||
width = size ? `${size}px` : '24px',
|
||||
class: className
|
||||
}: Props = $props()
|
||||
</script>
|
||||
|
||||
<svg {width} {height} viewBox="0 0 168 168" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M97.4434 164.242C93.2027 169.582 84.6042 166.656 84.502 159.837L83.0078 60.1013H150.07C162.217 60.1013 168.992 74.1309 161.439 83.644L97.4434 164.242Z" fill="url(#paint0_linear_210_201)"/>
|
||||
<path d="M97.4434 164.242C93.2027 169.582 84.6042 166.656 84.502 159.837L83.0078 60.1013H150.07C162.217 60.1013 168.992 74.1309 161.439 83.644L97.4434 164.242Z" fill="url(#paint1_linear_210_201)" fill-opacity="0.2"/>
|
||||
<path d="M70.1702 3.75823C74.411 -1.58278 83.0096 1.3437 83.1118 8.1628L83.7666 107.899H17.5434C5.39617 107.899 -1.37853 93.8692 6.17495 84.3561L70.1702 3.75823Z" fill="#3ECF8E"/>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_210_201" x1="83.0078" y1="82.2153" x2="142.611" y2="107.213" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#249361"/>
|
||||
<stop offset="1" stop-color="#3ECF8E"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear_210_201" x1="56.5831" y1="46.0351" x2="83.765" y2="97.2037" gradientUnits="userSpaceOnUse">
|
||||
<stop/>
|
||||
<stop offset="1" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<svg
|
||||
{width}
|
||||
{height}
|
||||
class={className}
|
||||
viewBox="0 0 168 168"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M97.4434 164.242C93.2027 169.582 84.6042 166.656 84.502 159.837L83.0078 60.1013H150.07C162.217 60.1013 168.992 74.1309 161.439 83.644L97.4434 164.242Z"
|
||||
fill="url(#paint0_linear_210_201)"
|
||||
/>
|
||||
<path
|
||||
d="M97.4434 164.242C93.2027 169.582 84.6042 166.656 84.502 159.837L83.0078 60.1013H150.07C162.217 60.1013 168.992 74.1309 161.439 83.644L97.4434 164.242Z"
|
||||
fill="url(#paint1_linear_210_201)"
|
||||
fill-opacity="0.2"
|
||||
/>
|
||||
<path
|
||||
d="M70.1702 3.75823C74.411 -1.58278 83.0096 1.3437 83.1118 8.1628L83.7666 107.899H17.5434C5.39617 107.899 -1.37853 93.8692 6.17495 84.3561L70.1702 3.75823Z"
|
||||
fill="#3ECF8E"
|
||||
/>
|
||||
<defs>
|
||||
<linearGradient
|
||||
id="paint0_linear_210_201"
|
||||
x1="83.0078"
|
||||
y1="82.2153"
|
||||
x2="142.611"
|
||||
y2="107.213"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stop-color="#249361" />
|
||||
<stop offset="1" stop-color="#3ECF8E" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="paint1_linear_210_201"
|
||||
x1="56.5831"
|
||||
y1="46.0351"
|
||||
x2="83.765"
|
||||
y2="97.2037"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop />
|
||||
<stop offset="1" stop-opacity="0" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
||||
Reference in New Issue
Block a user