feat: refactor favorite menu

This commit is contained in:
Ruben Fiszel
2022-12-04 13:23:29 +01:00
parent 60984a3ed8
commit c55fae54dd
9 changed files with 114 additions and 23 deletions
+7
View File
@@ -2337,6 +2337,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 apps
+8 -1
View File
@@ -25,6 +25,7 @@ use sql_builder::{bind::Bind, SqlBuilder};
use sqlx::{types::Uuid, FromRow};
use windmill_audit::{audit_log, ActionKind};
use windmill_common::{
apps::ListAppQuery,
error::{to_anyhow, Error, JsonResult, Result},
users::owner_to_token_owner,
utils::{not_found_if_none, paginate, Pagination, StripPath},
@@ -120,12 +121,13 @@ pub struct EditApp {
async fn list_apps(
authed: Authed,
Query(pagination): Query<Pagination>,
Query(lq): Query<ListAppQuery>,
Extension(user_db): Extension<UserDB>,
Path(w_id): Path<String>,
) -> JsonResult<Vec<ListableApp>> {
let (per_page, offset) = paginate(pagination);
let sqlb = SqlBuilder::select_from("app")
let mut sqlb = SqlBuilder::select_from("app")
.fields(&[
"app.id",
"app.workspace_id",
@@ -154,6 +156,11 @@ async fn list_apps(
.offset(offset)
.limit(per_page)
.clone();
if lq.starred_only.unwrap_or(false) {
sqlb.and_where_is_not_null("favorite.path");
}
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::<_, ListableApp>(&sql)
+14
View File
@@ -0,0 +1,14 @@
/*
* 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 serde::Deserialize;
#[derive(Deserialize)]
pub struct ListAppQuery {
pub starred_only: Option<bool>,
}
+1
View File
@@ -10,6 +10,7 @@ use std::net::SocketAddr;
use error::Error;
pub mod apps;
pub mod error;
pub mod external_ip;
pub mod flow_status;
@@ -8,7 +8,7 @@
export let pickCallback: (path: string, f: string) => void
export let loadItems: () => Promise<Item[] | undefined>
export let extraField: string = 'path'
export let extraField2: string | undefined
export let extraField2: string | undefined = undefined
export let itemName: string
export let closeOnClick = true
/** Displayed if the load function returns no items. */
@@ -88,6 +88,7 @@
}
goto(`?step=${step}`)
}
$: kind = script.kind as 'script' | 'trigger' | 'approval' | undefined
</script>
<UnsavedConfirmationModal />
@@ -290,7 +291,7 @@
bind:code={script.content}
lang={script.language}
{initialArgs}
kind={script.kind}
{kind}
/>
{:else if step === 3}
<CenteredPage>
@@ -0,0 +1,59 @@
<script lang="ts">
import { classNames } from '$lib/utils'
import { faCode, faStar, faTelevision, faWind } from '@fortawesome/free-solid-svg-icons'
import Icon from 'svelte-awesome'
import Menu from '../common/menu/Menu.svelte'
export let isCollapsed: boolean = false
export let favoriteLinks = [] as {
label: string
href: string
kind: 'script' | 'flow' | 'app'
}[]
</script>
<Menu placement="bottom-start" let:close noMinW>
<button
slot="trigger"
type="button"
class={classNames(
'group w-full flex items-center text-white hover:bg-gray-50 hover:text-gray-900 focus:ring-4 focus:outline-none focus:ring-gray-300 px-2 py-2 text-sm font-medium rounded-md h-8 '
)}
>
<Icon
data={faStar}
class={classNames('flex-shrink-0 h-4 w-4', isCollapsed ? '-mr-1' : 'mr-2')}
/>
{#if !isCollapsed}
<span class={classNames('whitespace-pre truncate')}>Favorites</span>
{/if}
</button>
<div class="divide-y divide-gray-100 overflow-hidden" role="none">
<table class="w-full">
{#each favoriteLinks ?? [] as favorite (favorite.href)}
<tr>
<a href={favorite.href} on:click={close} class="inline-flex flex-row px-4 py-2">
<td class="center-center">
{#if favorite.kind == 'script'}
<Icon data={faCode} />
{:else if favorite.kind == 'flow'}
<Icon data={faWind} />
{:else if favorite.kind == 'app'}
<Icon data={faTelevision} />
{/if}
</td>
<td class="text-gray-800 ml-2 grow text-xs center-center truncate">{favorite.label}</td>
</a>
</tr>
{/each}
</table>
<div class="py-1" role="none">
<div class="text-gray-600 block px-4 py-2 text-xs" role="menuitem" tabindex="-1">
Add Scripts/Flows/Apps here by starring them
</div>
</div>
</div>
</Menu>
@@ -9,7 +9,6 @@
faRobot,
faUsersCog,
faCog,
faStar,
faDollarSign
} from '@fortawesome/free-solid-svg-icons'
import { faDiscord, faGithub } from '@fortawesome/free-brands-svg-icons'
@@ -23,8 +22,6 @@
{ label: 'Resources', href: '/resources', icon: faCubes }
]
export let favoriteLinks = [] as { label: string; href: string }[]
$: secondaryMenuLinks = [
{ label: 'Schedules', href: '/schedules', icon: faCalendar },
{ label: 'Groups', href: '/groups', icon: faUsersCog },
@@ -51,21 +48,13 @@
export let isCollapsed: boolean = false
</script>
<div class="flex-1 flex flex-col py-4 overflow-x-hidden scrollbar-hidden">
<div class="flex-1 flex flex-col pb-4 overflow-x-hidden scrollbar-hidden">
<nav class="h-full flex justify-between flex-col px-2">
<div class="space-y-1 pt-4">
{#each mainMenuLinks as menuLink (menuLink.href)}
<MenuLink class="text-md" {...menuLink} {isCollapsed} />
{/each}
<div class="h-2" />
<div class="max-h-40 overflow-y-auto max-w-xs truncate">
<div class="flex flex-col max-h-min">
{#each favoriteLinks as menuLink (menuLink.href)}
<MenuLink class="text-xs max-w-xs truncate" {...menuLink} {isCollapsed} icon={faStar} />
{/each}
</div>
</div>
<div class="h-2" />
</div>
<div class="space-1-2">
{#each secondaryMenuLinks as menuLink (menuLink.href)}
+21 -8
View File
@@ -3,7 +3,7 @@
import Icon from 'svelte-awesome'
import UserMenu from '$lib/components/sidebar/UserMenu.svelte'
import { FlowService, OpenAPI, ScriptService } from '$lib/gen'
import { AppService, FlowService, OpenAPI, ScriptService } from '$lib/gen'
import { classNames } from '$lib/utils'
import WorkspaceMenu from '$lib/components/sidebar/WorkspaceMenu.svelte'
@@ -16,6 +16,7 @@
import SuperadminSettings from '$lib/components/SuperadminSettings.svelte'
import WindmillIcon from '$lib/components/icons/WindmillIcon.svelte'
import { page } from '$app/stores'
import FavoriteMenu from '$lib/components/sidebar/FavoriteMenu.svelte'
OpenAPI.WITH_CREDENTIALS = true
@@ -34,7 +35,7 @@
let innerWidth = window.innerWidth
let favoriteLinks = [] as { label: string; href: string }[]
let favoriteLinks = [] as { label: string; href: string; kind: 'app' | 'script' | 'flow' }[]
$: $workspaceStore && $starStore && loadFavorites()
async function loadFavorites() {
@@ -46,14 +47,25 @@
workspace: $workspaceStore ?? '',
starredOnly: true
})
const apps = await AppService.listApps({
workspace: $workspaceStore ?? '',
starredOnly: true
})
favoriteLinks = [
...scripts.map((s) => ({
label: s.summary || s.path,
href: `/scripts/run/${s.hash}`
href: `/scripts/run/${s.hash}`,
kind: 'script' as 'script'
})),
...flows.map((f) => ({
label: f.summary || f.path,
href: `/flows/run/${f.path}`
href: `/flows/run/${f.path}`,
kind: 'flow' as 'flow'
})),
...apps.map((f) => ({
label: f.summary || f.path,
href: `/apps/get/${f.path}`,
kind: 'app' as 'app'
}))
]
}
@@ -138,9 +150,10 @@
on:user-settings={() => userSettings.openDrawer()}
on:superadmin-settings={() => superadminSettings.openDrawer()}
/>
<FavoriteMenu {favoriteLinks} />
</div>
<SidebarContent {favoriteLinks} {isCollapsed} />
<SidebarContent {isCollapsed} />
</div>
</div>
</div>
@@ -155,7 +168,7 @@
<div class="flex-1 flex flex-col min-h-0 shadow-lg bg-[#2e3440]">
<button
on:click={() => {
isCollapsed = !isCollapsed
goto('/')
}}
>
<div
@@ -170,7 +183,6 @@
>
</div>
</button>
<div class="px-2 py-4 space-y-2 border-y border-gray-300">
<WorkspaceMenu {isCollapsed} />
<UserMenu
@@ -178,8 +190,9 @@
on:superadmin-settings={() => superadminSettings.openDrawer()}
{isCollapsed}
/>
<FavoriteMenu {favoriteLinks} />
</div>
<SidebarContent {favoriteLinks} {isCollapsed} />
<SidebarContent {isCollapsed} />
<div class="flex-shrink-0 flex px-4 pb-3.5 pt-3 border-t border-gray-300">
<button