From b3e1e8f82467ef4bd05834028f385c080096dc6d Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Fri, 9 Feb 2024 11:57:30 +0100 Subject: [PATCH] fix: improve on-boarding flow app --- .../20240209103543_update_app_new.down.sql | 1 + .../20240209103543_update_app_new.up.sql | 1165 +++++++++++++++++ frontend/src/lib/common.ts | 5 +- frontend/src/lib/components/ArgInput.svelte | 2 +- .../lib/components/LightweightArgInput.svelte | 39 +- .../src/lib/components/SchemaModal.svelte | 6 +- .../lib/components/StringTypeNarrowing.svelte | 15 +- .../src/routes/(root)/(logged)/+layout.svelte | 292 +++-- .../user/(user)/first-time/+page.svelte | 4 +- .../(user)/instance_settings/+page.svelte | 2 +- 10 files changed, 1364 insertions(+), 167 deletions(-) create mode 100644 backend/migrations/20240209103543_update_app_new.down.sql create mode 100644 backend/migrations/20240209103543_update_app_new.up.sql diff --git a/backend/migrations/20240209103543_update_app_new.down.sql b/backend/migrations/20240209103543_update_app_new.down.sql new file mode 100644 index 0000000000..d2f607c5b8 --- /dev/null +++ b/backend/migrations/20240209103543_update_app_new.down.sql @@ -0,0 +1 @@ +-- Add down migration script here diff --git a/backend/migrations/20240209103543_update_app_new.up.sql b/backend/migrations/20240209103543_update_app_new.up.sql new file mode 100644 index 0000000000..e69e01f1fa --- /dev/null +++ b/backend/migrations/20240209103543_update_app_new.up.sql @@ -0,0 +1,1165 @@ +-- Add up migration script here +-- Add up migration script here +-- Add up migration script here + +with _insert as +( +INSERT INTO + app_version(app_id, created_by, created_at, value) +VALUES + ( + ( + SELECT + id + FROM + app + WHERE + workspace_id = 'admins' + AND path = 'g/all/setup_app' + ), + 'admin@windmill.dev', + '2023-01-01 00:00:00.000000 +00:00', + '{ + "css": { + "htmlcomponent": { + "container": { + "class": "", + "style": "" + } + }, + "textcomponent": { + "text": { + "class": "", + "style": "" + }, + "container": { + "class": "", + "style": "" + } + }, + "imagecomponent": { + "image": { + "class": "", + "style": "" + } + }, + "containercomponent": { + "container": { + "class": "", + "style": "" + } + }, + "textinputcomponent": { + "input": { + "class": "", + "style": "" + } + }, + "schemaformcomponent": { + "label": { + "class": "", + "style": "" + }, + "container": { + "class": "", + "style": "" + }, + "description": { + "class": "", + "style": "" + } + }, + "buttoncomponent": { + "button": { + "style": "", + "class": "" + }, + "container": { + "style": "", + "class": "" + } + } + }, + "grid": [ + { + "3": { + "h": 2, + "w": 3, + "x": 0, + "y": 0, + "fixed": false + }, + "12": { + "h": 2, + "w": 11, + "x": 0, + "y": 1, + "fixed": false + }, + "id": "a", + "data": { + "id": "a", + "type": "textcomponent", + "customCss": { + "text": { + "class": "text-3xl font-semibold", + "style": "" + }, + "container": { + "class": "", + "style": "" + } + }, + "configuration": { + "style": { + "type": "static", + "value": "Body" + }, + "tooltip": { + "type": "static", + "value": "" + }, + "copyButton": { + "type": "static", + "value": false + } + }, + "componentInput": { + "eval": "Setup your Windmill Instance", + "type": "templatev2", + "value": "Hello ${ctx.username}", + "fieldType": "template", + "connections": [] + }, + "verticalAlignment": "center", + "horizontalAlignment": "left" + } + }, + { + "3": { + "h": 3, + "w": 3, + "x": 0, + "y": 2, + "fixed": false + }, + "12": { + "h": 2, + "w": 1, + "x": 11, + "y": 1, + "fixed": false + }, + "data": { + "id": "b", + "type": "imagecomponent", + "customCss": { + "image": { + "class": "", + "style": "" + } + }, + "configuration": { + "source": { + "type": "static", + "value": "/logo.svg" + }, + "altText": { + "type": "static", + "value": "" + }, + "imageFit": { + "type": "static", + "value": "contain" + }, + "sourceKind": { + "type": "static", + "value": "url" + } + } + }, + "id": "b" + }, + { + "3": { + "h": 11, + "w": 3, + "x": 0, + "y": 6, + "fixed": false + }, + "12": { + "h": 12, + "w": 6, + "x": 0, + "y": 6, + "fixed": false + }, + "id": "d", + "data": { + "id": "d", + "type": "containercomponent", + "customCss": { + "container": { + "class": "shadow-lg rounded-lg border p-2 mr-2", + "style": "" + } + }, + "configuration": {}, + "numberOfSubgrids": 1 + } + }, + { + "3": { + "h": 9, + "w": 3, + "x": 0, + "y": 17, + "fixed": false + }, + "12": { + "h": 12, + "w": 6, + "x": 6, + "y": 6, + "fixed": false + }, + "id": "f", + "data": { + "id": "f", + "type": "containercomponent", + "customCss": { + "container": { + "class": "shadow-lg rounded-lg border p-2 ml-2", + "style": "" + } + }, + "configuration": {}, + "numberOfSubgrids": 1 + } + }, + { + "3": { + "h": 2, + "w": 3, + "x": 0, + "y": 26, + "fixed": false + }, + "12": { + "h": 2, + "w": 9, + "x": 3, + "y": 19, + "fixed": false + }, + "id": "i", + "data": { + "id": "i", + "type": "buttoncomponent", + "customCss": { + "button": { + "class": "", + "style": "" + }, + "container": { + "class": "", + "style": "" + } + }, + "recomputeIds": [], + "configuration": { + "size": { + "type": "static", + "value": "lg" + }, + "color": { + "type": "static", + "value": "dark" + }, + "label": { + "type": "static", + "value": "Set admin account and hub sync" + }, + "onError": { + "type": "oneOf", + "selected": "setTab", + "configuration": { + "open": { + "id": { + "type": "static", + "value": "" + } + }, + "close": { + "id": { + "type": "static", + "value": "" + } + }, + "setTab": { + "setTab": { + "type": "static", + "value": [ + { + "id": "p", + "index": 0 + } + ] + } + }, + "gotoUrl": { + "url": { + "type": "static", + "value": "" + }, + "newTab": { + "type": "static", + "value": true + } + }, + "errorOverlay": {}, + "sendErrorToast": { + "message": { + "type": "static", + "value": "" + }, + "appendError": { + "type": "static", + "value": true + } + } + } + }, + "disabled": { + "expr": "c?.values?.new_email === '''' || c?.values?.password === ''''||c.valid === false ", + "type": "evalv2", + "value": false, + "connections": [ + { + "id": "values", + "componentId": "c" + }, + { + "id": "valid", + "componentId": "c" + } + ] + }, + "afterIcon": { + "type": "static", + "value": "Zap" + }, + "onSuccess": { + "type": "oneOf", + "selected": "gotoUrl", + "configuration": { + "none": {}, + "open": { + "id": { + "type": "static", + "value": "" + } + }, + "close": { + "id": { + "type": "static", + "value": "" + } + }, + "setTab": { + "setTab": { + "type": "static", + "value": [ + { + "id": "p", + "index": 1 + } + ] + } + }, + "gotoUrl": { + "url": { + "expr": "bg_1.result", + "type": "evalv2", + "value": "", + "connections": [ + { + "id": "result", + "componentId": "bg_1" + } + ] + }, + "newTab": { + "type": "static", + "value": false + } + }, + "openModal": { + "modalId": { + "type": "static", + "value": "" + } + }, + "sendToast": { + "message": { + "type": "static", + "value": "" + } + }, + "clearFiles": { + "id": { + "type": "static", + "value": "" + } + }, + "closeModal": { + "modalId": { + "type": "static", + "value": "" + } + } + } + }, + "beforeIcon": { + "type": "static", + "value": "" + }, + "fillContainer": { + "type": "static", + "value": false + }, + "triggerOnAppLoad": { + "type": "static", + "value": false + } + }, + "componentInput": { + "type": "runnable", + "fields": { + "oldEmail": { + "expr": "ctx.email", + "type": "evalv2", + "value": null, + "fieldType": "string", + "connections": [ + { + "id": "email", + "componentId": "ctx" + } + ] + }, + "newEmail": { + "expr": "c.values.new_email", + "type": "evalv2", + "value": null, + "fieldType": "string", + "connections": [ + { + "id": "values", + "componentId": "c" + } + ] + }, + "newPassword": { + "expr": "c.values.password", + "type": "evalv2", + "value": null, + "fieldType": "string", + "connections": [ + { + "id": "values", + "componentId": "c" + } + ] + }, + "enable_hub_sync": { + "expr": "h.result", + "type": "evalv2", + "value": null, + "fieldType": "boolean", + "connections": [ + { + "id": "result", + "componentId": "h" + } + ] + }, + "sync_now": { + "type": "evalv2", + "value": null, + "fieldType": "boolean", + "expr": "k.result", + "connections": [ + { + "componentId": "k", + "id": "result" + } + ] + } + }, + "runnable": { + "name": "Change Account", + "type": "runnableByName", + "inlineScript": { + "path": "g/all/setup_app/Change_Account", + "schema": { + "type": "object", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "required": [ + "oldEmail", + "newEmail", + "newPassword", + "enable_hub_sync", + "sync_now" + ], + "properties": { + "oldEmail": { + "default": null, + "description": "", + "type": "string" + }, + "newEmail": { + "default": null, + "description": "", + "type": "string" + }, + "newPassword": { + "default": null, + "description": "", + "type": "string" + }, + "enable_hub_sync": { + "default": null, + "description": "", + "type": "boolean" + }, + "sync_now": { + "default": null, + "description": "", + "type": "boolean" + } + } + }, + "content": "import * as wmill from \"https://deno.land/x/windmill@v1.99.0/mod.ts\";\n\nexport async function main(\n oldEmail: string,\n newEmail: string,\n newPassword: string,\n enable_hub_sync: boolean,\n sync_now: boolean,\n) {\n try {\n await wmill.UserService.createUserGlobally({\n requestBody: {\n email: newEmail,\n password: newPassword,\n super_admin: true,\n },\n });\n } catch (e) {\n throw Error(\"User already exists: \" + e.body);\n }\n\n let new_token;\n\n try {\n new_token = await wmill.UserService.login({\n requestBody: {\n email: newEmail,\n password: newPassword,\n },\n });\n } catch (e) {\n throw Error(\"Login failed: \" + e.body);\n }\n\n wmill.setClient(new_token, Deno.env.get(\"BASE_INTERNAL_URL\")!);\n\n if (sync_now) {\n try {\n await wmill.JobService.runScriptByPath({\n workspace: \"admins\",\n path: \"u/admin/hub_sync\",\n requestBody: {},\n });\n } catch (e) {\n throw Error(\"Hub sync failed:\" + e.body);\n }\n }\n\n if (enable_hub_sync) {\n try {\n await wmill.ScheduleService.createSchedule({\n workspace: \"admins\",\n requestBody: {\n path: \"g/all/hub_sync\",\n schedule: \"0 0 0 * * *\",\n script_path: \"u/admin/hub_sync\",\n is_flow: false,\n args: {},\n enabled: true,\n timezone: \"Etc/UTC\",\n },\n });\n } catch (e) {\n throw Error(\"Error creating schedule: \" + e.body);\n }\n }\n try {\n await wmill.UserService.globalUserDelete({ email: oldEmail });\n } catch (e) {\n throw Error(\"Deleting old account failed: \" + e.body);\n }\n}\n", + "language": "deno" + } + }, + "fieldType": "any", + "autoRefresh": false, + "recomputeOnInputChanged": false + }, + "verticalAlignment": "center", + "horizontalAlignment": "right" + } + }, + { + "3": { + "h": 8, + "w": 3, + "x": 0, + "y": 28, + "fixed": false + }, + "12": { + "h": 7, + "w": 12, + "x": 0, + "y": 21, + "fixed": false + }, + "id": "p", + "data": { + "id": "p", + "type": "conditionalwrapper", + "customCss": { + "container": { + "class": "", + "style": "" + } + }, + "conditions": [ + { + "expr": "i?.result.error", + "type": "evalv2", + "fieldType": "boolean", + "connections": [ + { + "componentId": "i", + "id": "result" + } + ] + }, + { + "expr": "true", + "type": "evalv2", + "fieldType": "boolean", + "connections": [] + } + ], + "configuration": {}, + "numberOfSubgrids": 2 + } + } + ], + "theme": { + "path": "f/app_themes/theme_0", + "type": "path" + }, + "subgrids": { + "d-0": [ + { + "3": { + "h": 1, + "w": 3, + "x": 0, + "y": 0, + "fixed": false + }, + "12": { + "h": 1, + "w": 12, + "x": 0, + "y": 0, + "fixed": false + }, + "id": "e", + "data": { + "id": "e", + "type": "textcomponent", + "customCss": { + "text": { + "class": "text-xl font-semibold px-2", + "style": "" + }, + "container": { + "class": "", + "style": "" + } + }, + "configuration": { + "style": { + "type": "static", + "value": "Body" + }, + "tooltip": { + "type": "static", + "value": "" + }, + "copyButton": { + "type": "static", + "value": false + } + }, + "componentInput": { + "eval": "Setup a secure account", + "type": "templatev2", + "value": "Hello ${ctx.username}", + "fieldType": "template", + "connections": [] + }, + "verticalAlignment": "center", + "horizontalAlignment": "left" + } + }, + { + "3": { + "h": 5, + "w": 3, + "x": 0, + "y": 2, + "fixed": false + }, + "12": { + "h": 5, + "w": 12, + "x": 0, + "y": 2, + "fixed": false + }, + "id": "c", + "data": { + "id": "c", + "type": "schemaformcomponent", + "customCss": { + "label": { + "class": "", + "style": "" + }, + "container": { + "class": "", + "style": "" + }, + "description": { + "class": "", + "style": "" + } + }, + "configuration": { + "largeGap": { + "type": "static", + "value": false + }, + "displayType": { + "type": "static", + "value": false + }, + "dynamicEnums": { + "type": "static", + "value": {} + }, + "defaultValues": { + "type": "static", + "value": {} + } + }, + "componentInput": { + "type": "static", + "value": { + "order": [ + "new_email", + "password" + ], + "required": [], + "properties": { + "new_email": { + "type": "string", + "format": "email", + "default": "", + "pattern": "^[\\w-.]+@([\\w-]+\\.)+[\\w-]{2,4}$", + "description": "" + }, + "password": { + "type": "string", + "description": "", + "pattern": "^..+$", + "default": "", + "customErrorMessage": "Must have at least 2 chars", + "password": true + } + } + }, + "fieldType": "schema" + } + } + }, + { + "3": { + "h": 1, + "w": 3, + "x": 0, + "y": 9, + "fixed": false + }, + "12": { + "h": 1, + "w": 12, + "x": 0, + "y": 10, + "fixed": false + }, + "id": "l", + "data": { + "id": "l", + "type": "textcomponent", + "customCss": { + "text": { + "class": "px-2", + "style": "" + }, + "container": { + "class": "", + "style": "" + } + }, + "configuration": { + "style": { + "type": "static", + "value": "Caption" + }, + "tooltip": { + "type": "static", + "value": "" + }, + "copyButton": { + "type": "static", + "value": false + } + }, + "componentInput": { + "eval": "Current email: ${ctx.email}", + "type": "templatev2", + "value": "Hello ${ctx.username}", + "fieldType": "template", + "connections": [ + { + "id": "email", + "componentId": "ctx" + } + ] + }, + "verticalAlignment": "top", + "horizontalAlignment": "left" + } + } + ], + "f-0": [ + { + "3": { + "h": 1, + "w": 3, + "x": 0, + "y": 0, + "fixed": false + }, + "12": { + "h": 1, + "w": 12, + "x": 0, + "y": 0, + "fixed": false + }, + "id": "g", + "data": { + "id": "g", + "type": "textcomponent", + "customCss": { + "text": { + "class": "text-xl font-semibold px-2", + "style": "" + }, + "container": { + "class": "", + "style": "" + } + }, + "configuration": { + "style": { + "type": "static", + "value": "Body" + }, + "tooltip": { + "type": "static", + "value": "" + }, + "copyButton": { + "type": "static", + "value": false + } + }, + "componentInput": { + "eval": "Hub Sync", + "type": "templatev2", + "value": "Hello ${ctx.username}", + "fieldType": "template", + "connections": [] + }, + "verticalAlignment": "center", + "horizontalAlignment": "left" + } + }, + { + "3": { + "h": 3, + "w": 3, + "x": 0, + "y": 2, + "fixed": false + }, + "12": { + "h": 6, + "w": 12, + "x": 0, + "y": 2, + "fixed": false + }, + "id": "m", + "data": { + "id": "m", + "type": "containercomponent", + "customCss": { + "container": { + "class": "", + "style": "" + } + }, + "configuration": {}, + "numberOfSubgrids": 1 + } + }, + { + "3": { + "h": 2, + "w": 3, + "x": 0, + "y": 6, + "fixed": false + }, + "12": { + "h": 1, + "w": 12, + "x": 0, + "y": 10, + "fixed": false + }, + "id": "n", + "data": { + "id": "n", + "type": "textcomponent", + "customCss": { + "text": { + "class": "px-2 xs", + "style": "" + }, + "container": { + "class": "", + "style": "" + } + }, + "configuration": { + "style": { + "type": "static", + "value": "Caption" + }, + "tooltip": { + "type": "static", + "value": "" + }, + "copyButton": { + "type": "static", + "value": false + } + }, + "componentInput": { + "eval": "The schedule synchronizes resource types from the Hub every day.", + "type": "templatev2", + "value": "Hello ${ctx.username}", + "fieldType": "template", + "connections": [] + }, + "verticalAlignment": "top", + "horizontalAlignment": "left" + } + } + ], + "m-0": [ + { + "3": { + "h": 1, + "w": 3, + "x": 0, + "y": 0, + "fixed": false + }, + "12": { + "h": 2, + "w": 12, + "x": 0, + "y": 0, + "fixed": false + }, + "data": { + "id": "k", + "type": "checkboxcomponent", + "customCss": { + "text": { + "class": "", + "style": "" + }, + "container": { + "class": "", + "style": "" + } + }, + "recomputeIds": [], + "configuration": { + "label": { + "type": "static", + "value": "Sync resource types now" + }, + "disabled": { + "type": "static", + "value": false + }, + "defaultValue": { + "type": "static", + "value": true + } + }, + "verticalAlignment": "center", + "horizontalAlignment": "left" + }, + "id": "k" + }, + { + "3": { + "h": 1, + "w": 3, + "x": 0, + "y": 1, + "fixed": false + }, + "12": { + "h": 2, + "w": 12, + "x": 0, + "y": 2, + "fixed": false + }, + "id": "h", + "data": { + "id": "h", + "type": "checkboxcomponent", + "customCss": { + "text": { + "class": "", + "style": "" + }, + "container": { + "class": "", + "style": "" + } + }, + "recomputeIds": [], + "configuration": { + "label": { + "type": "static", + "value": "Sync resource types every day" + }, + "disabled": { + "type": "static", + "value": false + }, + "defaultValue": { + "type": "static", + "value": true + } + }, + "verticalAlignment": "center", + "horizontalAlignment": "left" + } + } + ], + "p-0": [ + { + "3": { + "fixed": false, + "x": 0, + "y": 0, + "w": 2, + "h": 1 + }, + "12": { + "fixed": false, + "x": 0, + "y": 0, + "w": 12, + "h": 3 + }, + "data": { + "type": "alertcomponent", + "configuration": { + "type": { + "type": "static", + "value": "error" + }, + "title": { + "type": "static", + "value": "There were an error with your setup:" + }, + "description": { + "type": "evalv2", + "value": "Description", + "expr": "i?.result?.error?.message", + "connections": [ + { + "componentId": "i", + "id": "result" + } + ] + }, + "notRounded": { + "type": "static", + "value": false + }, + "tooltip": { + "type": "static", + "value": "" + }, + "size": { + "type": "static", + "value": "sm" + }, + "collapsible": { + "type": "static", + "value": false + }, + "initiallyCollapsed": { + "type": "static", + "value": false + } + }, + "customCss": { + "container": { + "class": "", + "style": "" + }, + "background": { + "class": "", + "style": "" + }, + "icon": { + "class": "", + "style": "" + }, + "title": { + "class": "", + "style": "" + }, + "description": { + "class": "", + "style": "" + } + }, + "verticalAlignment": "center", + "id": "j" + }, + "id": "j" + } + ], + "p-1": [] + }, + "fullscreen": false, + "norefreshbar": true, + "hiddenInlineScripts": [ + { + "name": "Background Runnable 0", + "type": "runnableByName", + "fields": {}, + "hidden": true + }, + { + "name": "Compute URL", + "type": "runnableByName", + "fields": {}, + "autoRefresh": true, + "inlineScript": { + "path": "u/faton/captivating_app/Compute_URL", + "content": "return ''/user/logout?rd='' + encodeURIComponent(''/user/login?email='' + c.values.new_email)", + "language": "frontend", + "refreshOn": [ + { + "id": "c", + "key": "values" + } + ], + "suggestedRefreshOn": [] + }, + "recomputeIds": [], + "recomputeOnInputChanged": true + } + ], + "unusedInlineScripts": [] +}') RETURNING id) +UPDATE app SET versions = ARRAY((select id from _insert)), policy = '{ "execution_mode": "viewer", "triggerables": {} }' +WHERE workspace_id = 'admins' AND path = 'g/all/setup_app'; diff --git a/frontend/src/lib/common.ts b/frontend/src/lib/common.ts index b9f794d777..199c40b18f 100644 --- a/frontend/src/lib/common.ts +++ b/frontend/src/lib/common.ts @@ -37,6 +37,7 @@ export interface SchemaProperty { properties?: { [name: string]: SchemaProperty } required?: string[] showExpr?: string + password?: boolean } export interface ModalSchemaProperty { @@ -58,6 +59,7 @@ export interface ModalSchemaProperty { schema?: Schema customErrorMessage?: string showExpr?: string + password?: boolean } export function modalToSchema(schema: ModalSchemaProperty): SchemaProperty { @@ -78,7 +80,8 @@ export function modalToSchema(schema: ModalSchemaProperty): SchemaProperty { currency: schema.currency, currencyLocale: schema.currencyLocale, multiselect: schema.multiselect, - showExpr: schema.showExpr + showExpr: schema.showExpr, + password: schema.password } } export type Schema = { diff --git a/frontend/src/lib/components/ArgInput.svelte b/frontend/src/lib/components/ArgInput.svelte index 2380b250de..0aea8cb231 100644 --- a/frontend/src/lib/components/ArgInput.svelte +++ b/frontend/src/lib/components/ArgInput.svelte @@ -607,7 +607,7 @@ {:else if inputCat == 'string'}
- {#if password} + {#if password || extra?.['password'] == true} {:else} {#key extra?.['minRows']} diff --git a/frontend/src/lib/components/LightweightArgInput.svelte b/frontend/src/lib/components/LightweightArgInput.svelte index 5536ebb02f..0c7f2798a0 100644 --- a/frontend/src/lib/components/LightweightArgInput.svelte +++ b/frontend/src/lib/components/LightweightArgInput.svelte @@ -17,6 +17,7 @@ import DateTimeInput from './DateTimeInput.svelte' import CurrencyInput from './apps/components/inputs/currency/CurrencyInput.svelte' import Multiselect from 'svelte-multiselect' + import Password from './Password.svelte' export let css: ComponentCustomCSS<'schemaformcomponent'> | undefined = undefined export let label: string = '' @@ -395,23 +396,27 @@ {:else if inputCat == 'string'}
-