diff --git a/backend/migrations/20221128210151_favorites.down.sql b/backend/migrations/20221128210151_favorites.down.sql new file mode 100644 index 0000000000..d2f607c5b8 --- /dev/null +++ b/backend/migrations/20221128210151_favorites.down.sql @@ -0,0 +1 @@ +-- Add down migration script here diff --git a/backend/migrations/20221128210151_favorites.up.sql b/backend/migrations/20221128210151_favorites.up.sql new file mode 100644 index 0000000000..3aa002f66a --- /dev/null +++ b/backend/migrations/20221128210151_favorites.up.sql @@ -0,0 +1,11 @@ +-- Add up migration script here + +CREATE TYPE FAVORITE_KIND AS ENUM ('app', 'script', 'flow'); + +CREATE TABLE favorite ( + usr VARCHAR(50) NOT NULL, + workspace_id VARCHAR(50) NOT NULL, + path VARCHAR(255) NOT NULL, + favorite_kind FAVORITE_KIND NOT NULL, + PRIMARY KEY (usr, workspace_id, favorite_kind, path) +); \ No newline at end of file diff --git a/backend/sqlx-data.json b/backend/sqlx-data.json index 4c5da1367c..7f50d7c5e3 100644 --- a/backend/sqlx-data.json +++ b/backend/sqlx-data.json @@ -580,6 +580,32 @@ }, "query": "SELECT workspace.id, workspace.name, usr.username\n FROM workspace, usr WHERE usr.workspace_id = workspace.id AND usr.email = $1 AND deleted = false" }, + "22e14fc3bb5d8cf3006f0002e8522b8cc0b2fece43f03c0f025e7acefa0d4f32": { + "describe": { + "columns": [], + "nullable": [], + "parameters": { + "Left": [ + "Text", + "Text", + "Text", + { + "Custom": { + "kind": { + "Enum": [ + "app", + "script", + "flow" + ] + }, + "name": "favorite_kind" + } + } + ] + } + }, + "query": "DELETE FROM favorite WHERE workspace_id = $1 AND usr = $2 AND path = $3 AND favorite_kind = $4" + }, "23086afd75927486884944e48b768e956d1fd77ce08c6f345fcde083b1e9bbf1": { "describe": { "columns": [ @@ -1719,6 +1745,32 @@ }, "query": "UPDATE queue SET canceled = true, canceled_by = $1, canceled_reason = $2 WHERE parent_job = $3 AND workspace_id = $4 RETURNING id" }, + "653685b39d93008762818d0518b953632040122a9af98332d3fd1d12244b1b80": { + "describe": { + "columns": [], + "nullable": [], + "parameters": { + "Left": [ + "Varchar", + "Varchar", + "Varchar", + { + "Custom": { + "kind": { + "Enum": [ + "app", + "script", + "flow" + ] + }, + "name": "favorite_kind" + } + } + ] + } + }, + "query": "INSERT INTO favorite (workspace_id, usr, path, favorite_kind) VALUES ($1, $2, $3, $4) ON CONFLICT DO NOTHING" + }, "6c63bbcb45d3f51eccaea52ec862700e1f1c2426d823abd951e1eea4fd9b85aa": { "describe": { "columns": [], diff --git a/backend/windmill-api/openapi.yaml b/backend/windmill-api/openapi.yaml index f69ea7b0a3..20c019621e 100644 --- a/backend/windmill-api/openapi.yaml +++ b/backend/windmill-api/openapi.yaml @@ -1748,6 +1748,13 @@ paths: in: query schema: type: string + - name: starred_only + description: | + (default false) + show only the starred items + in: query + schema: + type: boolean responses: "200": description: All available scripts @@ -2191,6 +2198,13 @@ paths: in: query schema: type: boolean + - name: starred_only + description: | + (default false) + show only the starred items + in: query + schema: + type: boolean responses: "200": description: All available flow @@ -3572,6 +3586,52 @@ paths: "404": description: capture does not exist for this flow + /w/{workspace}/favorites/star: + post: + summary: star item + operationId: star + tags: + - favorite + parameters: + - $ref: "#/components/parameters/WorkspaceId" + requestBody: + content: + application/json: + schema: + type: object + properties: + path: + type: string + favorite_kind: + type: string + enum: [flow, app, script] + responses: + "200": + description: star item + + /w/{workspace}/favorites/unstar: + post: + summary: unstar item + operationId: unstar + tags: + - favorite + parameters: + - $ref: "#/components/parameters/WorkspaceId" + requestBody: + content: + application/json: + schema: + type: object + properties: + path: + type: string + favorite_kind: + type: string + enum: [flow, app, script] + responses: + "200": + description: unstar item + components: securitySchemes: bearerAuth: @@ -3808,6 +3868,8 @@ components: kind: type: string enum: [script, failure, trigger, command, approval] + starred: + type: boolean required: - hash - path @@ -3821,6 +3883,7 @@ components: - extra_perms - language - kind + - starred ScriptArgs: type: object @@ -4696,6 +4759,8 @@ components: type: object additionalProperties: type: boolean + starred: + type: boolean required: - path - edited_by diff --git a/backend/windmill-api/src/favorite.rs b/backend/windmill-api/src/favorite.rs new file mode 100644 index 0000000000..7591ee5baf --- /dev/null +++ b/backend/windmill-api/src/favorite.rs @@ -0,0 +1,75 @@ +/* + * Author: Ruben Fiszel + * Copyright: Windmill Labs, Inc 2022 + * This file and its contents are licensed under the AGPLv3 License. + * Please see the included NOTICE for copyright information and + * LICENSE-AGPL for a copy of the license. + */ + +use crate::{db::DB, users::Authed}; +use axum::{ + extract::{Extension, Path}, + routing::post, + Json, Router, +}; +use windmill_common::error::Result; + +use serde::{Deserialize, Serialize}; + +pub fn workspaced_service() -> Router { + Router::new() + .route("/star", post(star)) + .route("/unstar", post(unstar)) +} + +#[derive(sqlx::Type, Serialize, Deserialize, Debug, PartialEq, Clone)] +#[sqlx(type_name = "FAVORITE_KIND", rename_all = "lowercase")] +#[serde(rename_all(serialize = "lowercase", deserialize = "lowercase"))] +pub enum FavoriteKind { + Script, + Flow, + App, +} +#[derive(Deserialize)] +pub struct Favorite { + pub favorite_kind: FavoriteKind, + pub path: String, +} + +async fn star( + authed: Authed, + Extension(db): Extension, + Path(w_id): Path, + Json(Favorite { favorite_kind, path }): Json, +) -> Result { + sqlx::query!( + "INSERT INTO favorite (workspace_id, usr, path, favorite_kind) VALUES ($1, $2, $3, $4) ON CONFLICT DO NOTHING", + &w_id, + authed.username, + path, + favorite_kind: FavoriteKind, + ) + .execute(&db) + .await?; + + Ok(format!("Starred {}", path)) +} + +async fn unstar( + authed: Authed, + Extension(db): Extension, + Path(w_id): Path, + Json(Favorite { favorite_kind, path }): Json, +) -> Result { + sqlx::query!( + "DELETE FROM favorite WHERE workspace_id = $1 AND usr = $2 AND path = $3 AND favorite_kind = $4", + &w_id, + authed.username, + path, + favorite_kind: FavoriteKind, + ) + .execute(&db) + .await?; + + Ok(format!("Starred {}", path)) +} diff --git a/backend/windmill-api/src/flows.rs b/backend/windmill-api/src/flows.rs index 4c58dd3d96..1c1f7b8d0f 100644 --- a/backend/windmill-api/src/flows.rs +++ b/backend/windmill-api/src/flows.rs @@ -20,7 +20,7 @@ use sqlx::{Postgres, Transaction}; use windmill_audit::{audit_log, ActionKind}; use windmill_common::{ error::{self, to_anyhow, Error, JsonResult, Result}, - flows::{Flow, ListFlowQuery, NewFlow}, + flows::{Flow, ListFlowQuery, ListableFlow, NewFlow}, utils::{ http_get_from_hub, list_elems_from_hub, not_found_if_none, paginate, Pagination, StripPath, }, @@ -54,24 +54,30 @@ async fn list_flows( Path(w_id): Path, Query(pagination): Query, Query(lq): Query, -) -> JsonResult> { +) -> JsonResult> { let (per_page, offset) = paginate(pagination); let mut sqlb = SqlBuilder::select_from("flow as o") .fields(&[ - "workspace_id", - "path", + "o.workspace_id", + "o.path", "summary", "description", - "'{}'::jsonb as value", "edited_by", "edited_at", "archived", - "null schema", "extra_perms", + "favorite.path IS NOT NULL as starred", ]) + .left() + .join("favorite") + .on( + "favorite.favorite_kind = 'flow' AND favorite.path = o.path AND favorite.usr = ?" + .bind(&authed.username), + ) + .order_desc("favorite.path IS NOT NULL") .order_by("edited_at", lq.order_desc.unwrap_or(true)) - .and_where("workspace_id = ? OR workspace_id = 'starter'".bind(&w_id)) + .and_where("o.workspace_id = ? OR o.workspace_id = 'starter'".bind(&w_id)) .offset(offset) .limit(per_page) .clone(); @@ -88,10 +94,15 @@ async fn list_flows( if let Some(cb) = &lq.edited_by { sqlb.and_where_eq("edited_by", "?".bind(cb)); } + if let Some(so) = &lq.starred_only { + sqlb.and_where_eq("starred", "?".bind(so)); + } let sql = sqlb.sql().map_err(|e| Error::InternalErr(e.to_string()))?; let mut tx = user_db.begin(&authed).await?; - let rows = sqlx::query_as::<_, Flow>(&sql).fetch_all(&mut tx).await?; + let rows = sqlx::query_as::<_, ListableFlow>(&sql) + .fetch_all(&mut tx) + .await?; tx.commit().await?; Ok(Json(rows)) } diff --git a/backend/windmill-api/src/lib.rs b/backend/windmill-api/src/lib.rs index 60f44128c9..bce87fecb6 100644 --- a/backend/windmill-api/src/lib.rs +++ b/backend/windmill-api/src/lib.rs @@ -27,6 +27,7 @@ mod apps; mod audit; mod capture; mod db; +mod favorite; mod flows; mod granular_acls; mod groups; @@ -123,7 +124,8 @@ pub async fn run_server( .nest("/workspaces", workspaces::workspaced_service()) .nest("/flows", flows::workspaced_service()) .nest("/capture", capture::workspaced_service()) - .nest("/apps", apps::workspaced_service()), + .nest("/apps", apps::workspaced_service()) + .nest("/favorites", favorite::workspaced_service()), ) .nest("/workspaces", workspaces::global_service()) .nest( diff --git a/backend/windmill-api/src/scripts.rs b/backend/windmill-api/src/scripts.rs index 2a0d2dcba3..60f9d86aa9 100644 --- a/backend/windmill-api/src/scripts.rs +++ b/backend/windmill-api/src/scripts.rs @@ -31,7 +31,8 @@ use std::{ use windmill_common::{ error::{Error, JsonResult, Result}, scripts::{ - to_i64, HubScript, ListScriptQuery, NewScript, Script, ScriptHash, ScriptKind, ScriptLang, + to_i64, HubScript, ListScriptQuery, ListableScript, NewScript, Script, ScriptHash, + ScriptKind, ScriptLang, }, users::owner_to_token_owner, utils::{ @@ -76,32 +77,37 @@ async fn list_scripts( Path(w_id): Path, Query(pagination): Query, Query(lq): Query, -) -> JsonResult> { +) -> JsonResult> { let (per_page, offset) = paginate(pagination); let mut sqlb = SqlBuilder::select_from("script as o") .fields(&[ - "workspace_id", + "o.workspace_id", "hash", - "path", + "o.path", "array_remove(array[parent_hashes[1]], NULL) as parent_hashes", "summary", "description", - "'' as content", "created_by", "created_at", "archived", - "null as schema", "deleted", "is_template", "extra_perms", - "null as lock", "CASE WHEN lock_error_logs IS NOT NULL THEN 'error' ELSE null END as lock_error_logs", "language", "kind", + "favorite.path IS NOT NULL as starred", ]) + .left() + .join("favorite") + .on( + "favorite.favorite_kind = 'script' AND favorite.path = o.path AND favorite.usr = ?" + .bind(&authed.username), + ) + .order_desc("favorite.path IS NOT NULL") .order_by("created_at", lq.order_desc.unwrap_or(true)) - .and_where("workspace_id = ? OR workspace_id = 'starter'".bind(&w_id)) + .and_where("o.workspace_id = ? OR o.workspace_id = 'starter'".bind(&w_id)) .offset(offset) .limit(per_page) .clone(); @@ -110,7 +116,8 @@ async fn list_scripts( sqlb.and_where_eq( "created_at", "(select max(created_at) from script where o.path = path - AND (workspace_id = $1 OR workspace_id = 'starter'))", + AND (workspace_id = ? OR workspace_id = 'starter'))" + .bind(&w_id), ); } else { sqlb.and_where_eq("archived", false); @@ -139,10 +146,15 @@ async fn list_scripts( if let Some(k) = &lq.kind { sqlb.and_where_eq("kind", "?".bind(&k.to_lowercase())); } + if let Some(so) = &lq.starred_only { + sqlb.and_where_eq("starred", "?".bind(so)); + } let sql = sqlb.sql().map_err(|e| Error::InternalErr(e.to_string()))?; let mut tx = user_db.begin(&authed).await?; - let rows = sqlx::query_as::<_, Script>(&sql).fetch_all(&mut tx).await?; + let rows = sqlx::query_as::<_, ListableScript>(&sql) + .fetch_all(&mut tx) + .await?; tx.commit().await?; Ok(Json(rows)) } diff --git a/backend/windmill-common/src/flows.rs b/backend/windmill-common/src/flows.rs index 5c2d9d8899..744a4e5a7b 100644 --- a/backend/windmill-common/src/flows.rs +++ b/backend/windmill-common/src/flows.rs @@ -30,6 +30,20 @@ pub struct Flow { pub extra_perms: serde_json::Value, } +#[derive(Serialize)] +#[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))] +pub struct ListableFlow { + pub workspace_id: String, + pub path: String, + pub summary: String, + pub description: String, + pub edited_by: String, + pub edited_at: chrono::DateTime, + pub archived: bool, + pub extra_perms: serde_json::Value, + pub starred: bool, +} + #[derive(Deserialize)] #[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))] pub struct NewFlow { @@ -224,4 +238,5 @@ pub struct ListFlowQuery { pub show_archived: Option, pub order_by: Option, pub order_desc: Option, + pub starred_only: Option, } diff --git a/backend/windmill-common/src/scripts.rs b/backend/windmill-common/src/scripts.rs index f631e95676..e3a6cb1f40 100644 --- a/backend/windmill-common/src/scripts.rs +++ b/backend/windmill-common/src/scripts.rs @@ -126,6 +126,27 @@ pub struct Script { pub kind: ScriptKind, } +#[derive(Serialize)] +#[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))] +pub struct ListableScript { + pub workspace_id: String, + pub hash: ScriptHash, + pub path: String, + pub parent_hashes: Option, + pub summary: String, + pub description: String, + pub created_by: String, + pub created_at: chrono::DateTime, + pub archived: bool, + pub deleted: bool, + pub is_template: bool, + pub extra_perms: serde_json::Value, + pub lock_error_logs: Option, + pub language: ScriptLang, + pub kind: ScriptKind, + pub starred: bool, +} + #[derive(Serialize, Deserialize, Debug)] #[cfg_attr(feature = "sqlx", derive(sqlx::Type))] #[cfg_attr(feature = "sqlx", sqlx)] @@ -168,6 +189,7 @@ pub struct ListScriptQuery { pub order_desc: Option, pub is_template: Option, pub kind: Option, + pub starred_only: Option, } pub fn to_i64(s: &str) -> crate::error::Result { diff --git a/frontend/package-lock.json b/frontend/package-lock.json index ff09db9856..8aec21926a 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -60,6 +60,7 @@ "svelte-grid": "^5.1.1", "svelte-heros": "^2.3.5", "svelte-highlight": "^6.2.1", + "svelte-lucide": "^0.2.0", "svelte-markdown": "^0.2.3", "svelte-overlay": "^1.4.1", "svelte-popperjs": "^1.3.2", @@ -6104,6 +6105,12 @@ "svelte": ">=3.19.0" } }, + "node_modules/svelte-lucide": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/svelte-lucide/-/svelte-lucide-0.2.0.tgz", + "integrity": "sha512-Ki+M3rHNEcopLLjWzSfWiE4YumevBqqVfSQeWjsq1ZsZJrXyiEhh0qJB0Gb7Oj3OZVFo3uSOOMMQOxYHAVxLvQ==", + "dev": true + }, "node_modules/svelte-markdown": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/svelte-markdown/-/svelte-markdown-0.2.3.tgz", @@ -11316,6 +11323,12 @@ "dev": true, "requires": {} }, + "svelte-lucide": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/svelte-lucide/-/svelte-lucide-0.2.0.tgz", + "integrity": "sha512-Ki+M3rHNEcopLLjWzSfWiE4YumevBqqVfSQeWjsq1ZsZJrXyiEhh0qJB0Gb7Oj3OZVFo3uSOOMMQOxYHAVxLvQ==", + "dev": true + }, "svelte-markdown": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/svelte-markdown/-/svelte-markdown-0.2.3.tgz", diff --git a/frontend/package.json b/frontend/package.json index 6624d7fe6e..5f9657ecac 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -45,6 +45,7 @@ "svelte-grid": "^5.1.1", "svelte-heros": "^2.3.5", "svelte-highlight": "^6.2.1", + "svelte-lucide": "^0.2.0", "svelte-markdown": "^0.2.3", "svelte-overlay": "^1.4.1", "svelte-popperjs": "^1.3.2", diff --git a/frontend/src/lib/components/FlowBox.svelte b/frontend/src/lib/components/FlowBox.svelte new file mode 100644 index 0000000000..572755b6a8 --- /dev/null +++ b/frontend/src/lib/components/FlowBox.svelte @@ -0,0 +1,147 @@ + + + +
+
+ + + {#if marked} + {@html marked} + {:else} + {!summary || summary.length == 0 ? path : summary} + {/if} +
+
+
{path} + dispatch('change')} /> + +
+
+
+ { + shareModal.openDrawer(path) + }, + disabled: !canWrite + }, + { + displayName: 'Archive', + icon: faArchive, + action: () => { + path ? archiveFlow(path) : null + }, + type: 'delete', + disabled: !canWrite + } + ]} + /> +
+
+ +
+ {#if canWrite} +
+ +
+ {:else} +
+ +
+ {/if} +
+
diff --git a/frontend/src/lib/components/ScriptBox.svelte b/frontend/src/lib/components/ScriptBox.svelte new file mode 100644 index 0000000000..9726bacb31 --- /dev/null +++ b/frontend/src/lib/components/ScriptBox.svelte @@ -0,0 +1,157 @@ + + + +
+
+ + + {#if marked} + {@html marked} + {:else} + {!summary || summary.length == 0 ? path : summary} + {/if} +
+
+
+ {path} + dispatch('change')} /> + +
+ {#if kind != 'script'} + {kind} + {/if} + {#if lock_error_logs} + Deployment error + {/if} +
+
+
+
+ { + shareModal.openDrawer(path) + }, + disabled: !canWrite + }, + { + displayName: 'Archive', + icon: faArchive, + action: () => { + path ? archiveScript(path) : null + }, + type: 'delete', + disabled: !canWrite + } + ]} + /> +
+
+ +
+ {#if canWrite} +
+ +
+ {:else} +
+ +
+ {/if} +
+
+
diff --git a/frontend/src/lib/components/SearchItems.svelte b/frontend/src/lib/components/SearchItems.svelte index b781a549a9..806d263229 100644 --- a/frontend/src/lib/components/SearchItems.svelte +++ b/frontend/src/lib/components/SearchItems.svelte @@ -5,12 +5,12 @@ export let items: any[] export let f: (item: any) => string export let filteredItems: (any & { marked: string })[] + export let opts: uFuzzy.Options = {} - let opts = {} let uf = new uFuzzy(opts) $: plaintextItems = items.map((item) => f(item)) - $: plaintextItems && filter != undefined && setTimeout(() => filterItems(), 100) + $: plaintextItems && filter != undefined && setTimeout(() => filterItems(), 0) function filterItems() { if (filter.length == 0) { @@ -24,6 +24,7 @@ let order = uf.sort(info, plaintextItems, filter) let result: any[] = [] + for (let i = 0; i < order.length; i++) { let infoIdx = order[i] result.push({ diff --git a/frontend/src/lib/components/Star.svelte b/frontend/src/lib/components/Star.svelte new file mode 100644 index 0000000000..4c4a9b41ba --- /dev/null +++ b/frontend/src/lib/components/Star.svelte @@ -0,0 +1,41 @@ + + + diff --git a/frontend/src/lib/components/flows/CreateActions.svelte b/frontend/src/lib/components/flows/CreateActionsFlow.svelte similarity index 89% rename from frontend/src/lib/components/flows/CreateActions.svelte rename to frontend/src/lib/components/flows/CreateActionsFlow.svelte index 107088ebcc..50985a5920 100644 --- a/frontend/src/lib/components/flows/CreateActions.svelte +++ b/frontend/src/lib/components/flows/CreateActionsFlow.svelte @@ -1,6 +1,6 @@ - -
-
- Flows allow you to streamline complex processes and operations by chaining simple steps - together. Each Flow is composed of one or more steps. -
-
- - - -
-
diff --git a/frontend/src/lib/components/landing/FlowLandingBox.svelte b/frontend/src/lib/components/landing/FlowLandingBox.svelte deleted file mode 100644 index 4499a7fd6d..0000000000 --- a/frontend/src/lib/components/landing/FlowLandingBox.svelte +++ /dev/null @@ -1,42 +0,0 @@ - - - goto(`/flows/get/${flow.path}`)} -> -
{flow.summary || flow.path}
- -
-
{flow.path}
-
- -
- - - -
-
diff --git a/frontend/src/lib/components/landing/RessourceGettingStarted.svelte b/frontend/src/lib/components/landing/RessourceGettingStarted.svelte deleted file mode 100644 index 6a9a6f5191..0000000000 --- a/frontend/src/lib/components/landing/RessourceGettingStarted.svelte +++ /dev/null @@ -1,15 +0,0 @@ - - -
-
- Connect to apps like Slack, Google Drive or Airtable using OAuth. -
- -
diff --git a/frontend/src/lib/components/landing/ScriptBox.svelte b/frontend/src/lib/components/landing/ScriptBox.svelte deleted file mode 100644 index af980cbc55..0000000000 --- a/frontend/src/lib/components/landing/ScriptBox.svelte +++ /dev/null @@ -1,51 +0,0 @@ - - - -
{script.summary || script.path}
- -
-
{script.path}
-
-
-
- {#if script.kind !== 'script'} - - {script.kind} - - {/if} -
-
- - - -
-
diff --git a/frontend/src/lib/components/landing/ScriptGettingStarted.svelte b/frontend/src/lib/components/landing/ScriptGettingStarted.svelte deleted file mode 100644 index a23d35a8dc..0000000000 --- a/frontend/src/lib/components/landing/ScriptGettingStarted.svelte +++ /dev/null @@ -1,39 +0,0 @@ - - -
-
- - - -
-
diff --git a/frontend/src/lib/components/scripts/CreateActions.svelte b/frontend/src/lib/components/scripts/CreateActionsScript.svelte similarity index 81% rename from frontend/src/lib/components/scripts/CreateActions.svelte rename to frontend/src/lib/components/scripts/CreateActionsScript.svelte index 02dd572dd9..82efedf77e 100644 --- a/frontend/src/lib/components/scripts/CreateActions.svelte +++ b/frontend/src/lib/components/scripts/CreateActionsScript.svelte @@ -1,6 +1,6 @@ - - - - x.summary + ' (' + x.path + ')'} -/> - - - -
- - {#if flowViewerFlow?.flow} - - {/if} -
-
- - - -
- -
-
- - - Workspace - Hub - - -
- {#if tab != 'hub'} - - -
- {#each owners as owner} - { - ownerFilter = ownerFilter == owner ? undefined : owner - }} - color={owner === ownerFilter ? 'blue' : 'gray'} - > - {owner} - {#if owner === ownerFilter}✗{/if} - - {/each} -
- {/if} - - {#if tab == 'workspace'} - - {:else} - viewFlow(e.detail)} /> - {/if} - - - { - loadFlows() - }} -/> diff --git a/frontend/src/routes/index.svelte b/frontend/src/routes/index.svelte index d73263dd76..28b67632c3 100644 --- a/frontend/src/routes/index.svelte +++ b/frontend/src/routes/index.svelte @@ -1,39 +1,150 @@ - -

Home

-
- {#if $workspaceStore == 'demo'} - The demo workspace shared in which all users get invited. - {:else if $workspaceStore == 'starter'} - The starter workspace has all its elements (variables, resources, scripts, flows) shared - across all other workspaces. Useful to seed workspace with common elements within your - organization. + (x.summary ? x.summary + ' (' + x.path + ')' : x.path)} + {opts} +/> + + { + loadScripts() + }} +/> + + { + loadFlows() + }} +/> + + + +
+ + +
+
+ + + +
+ + {#if flowViewerFlow?.flow} + {/if} -
-

- Scripts -

- + + -
Latest scripts:
- -
- {#each scripts as script} - - {/each} - - All scripts - - - -
+ + {#if $workspaceStore == 'demo'} +
+ The demo workspace shared in which all users get invited. + {:else if $workspaceStore == 'starter'} +
+ + The starter workspace has all its elements (variables, resources, scripts, flows) shared + across all other workspaces. Useful to seed workspace with common elements within your + organization. + {/if} + +
+ +
-
-

- Flows -

- -
Latest flows:
+ - -
- {#each flows as flow} - - {/each} - - All flows - - - -
-
-
-

- Resources -

+
+ + Workspace + Hub Scripts + Hub Flows + +
+
+
+ {#if tab == 'workspace'} +
+ +
- {#if resources.length === 0} - +
+ + All + Scripts + Flows + +
+
+ {#each owners as owner} + { + ownerFilter = ownerFilter == owner ? undefined : owner + }} + color={owner === ownerFilter ? 'blue' : 'gray'} + > + {owner} + {#if owner === ownerFilter}✗{/if} + + {/each} +
+ +
+ {#if !loading} + {#each filter != '' ? filteredItems : preFilteredItems as item (item.path)} + {#if item.type == 'script'} + + {:else if item.type == 'flow'} + + {/if} + {/each} + {:else} + {#each Array(10).fill(0) as sk} + + {/each} + {/if} +
+ {:else if tab == 'hubscripts'} + viewCode(e.detail)} /> + {:else if tab == 'hubflows'} + viewFlow(e.detail)} /> {/if}
@@ -149,7 +371,7 @@
- + {#each jobs.splice(0, 3) as job} {/each} diff --git a/frontend/src/routes/scripts.svelte b/frontend/src/routes/scripts.svelte deleted file mode 100644 index 7909018489..0000000000 --- a/frontend/src/routes/scripts.svelte +++ /dev/null @@ -1,304 +0,0 @@ - - - - - x.summary + ' (' + x.path + ')'} -/> - - - -
- - -
-
- - - - - - - - Workspace - Hub - - -
- - {#if tab == 'workspace'} - - -
- {#each owners as owner} - { - ownerFilter = ownerFilter == owner ? undefined : owner - }} - color={owner === ownerFilter ? 'blue' : 'gray'} - > - {owner} - {#if owner === ownerFilter}✗{/if} - - {/each} -
- - {:else} - viewCode(e.detail)} /> - {/if} - - - { - loadScripts() - }} -/>