Sidebar rework (#490)

* feat(frontend): Add runs to landing page + fix responsive issues

* feat(frontend): Sidebar done

* feat(frontend): Align all pages to the new layout

* feat(frontend): Make scripts and flows box clickable

* feat(frontend): Revert the sidebar color

* feat(frontend): Restore missing workspace menu + fix minor UI issues
This commit is contained in:
Faton Ramadani
2022-08-26 17:15:27 +02:00
committed by GitHub
parent 9c9f0dadb5
commit 73a7bd896c
16 changed files with 518 additions and 364 deletions

View File

@@ -1,5 +1,5 @@
<div class="max-w-screen-lg flex grow w-full h-full xl:-ml-20 mr-2 lg:mr-0">
<div class="grow w-full h-full mt-4">
<div class="py-6">
<div class="max-w-6xl mx-auto px-4 sm:px-6 md:px-8">
<slot />
</div>
</div>

View File

@@ -154,8 +154,8 @@
})
</script>
<div class="flex flex-row w-full h-full justify-between">
<div class={`flex flex-col mb-96 m-auto w-full sm:w-3/4 lg:w-2/3`}>
<div class="flex flex-row w-full h-full justify-between ">
<div class={`flex flex-col mb-96 m-auto w-full`}>
<!-- Nav between steps-->
<div class="justify-between flex flex-row w-full my-4">
<Breadcrumb>

View File

@@ -103,9 +103,7 @@
})
</script>
<div
class="flex flex-col h-screen max-w-screen-lg xl:-ml-20 xl:pl-4 w-full -mt-4 pt-4 md:mx-10 pr-2"
>
<div class="flex flex-col h-screen ">
<!-- Nav between steps-->
<div class="flex flex-col w-full">
<div class="justify-between flex flex-row drop-shadow-sm w-full mt-4">

View File

@@ -6,7 +6,8 @@
</script>
<div
class="border p-4 rounded-sm shadow-sm space-y-2 hover:border-blue-600 flex flex-col justify-between"
class="border p-4 rounded-sm shadow-sm space-y-2 hover:border-blue-600 flex flex-col justify-between cursor-pointer"
on:click={() => goto(`/flows/get/${flow.path}`)}
>
<div class="font-bold">{flow.summary}</div>
@@ -18,7 +19,7 @@
class="inline-flex space-x-2 space-x-reverse flex-row-reverse w-full bottom-4 right-4 flex-wrap"
>
<button
on:click={() => goto(`/flows/edit/${flow.path}?step=2`)}
on:click|stopPropagation={() => goto(`/flows/edit/${flow.path}?step=2`)}
type="button"
class="inline-flex items-center text-gray-700 bg-transparent border border-gray-700 hover:bg-gray-800 hover:text-white focus:ring-4 focus:outline-none focus:ring-gray-300 font-medium rounded-lg text-xs px-3 py-1.5 text-center"
data-dismiss-target="#alert-additional-content-5"
@@ -41,7 +42,7 @@
</button>
<button
on:click={() => goto(`/flows/run/${flow.path}`)}
on:click|stopPropagation={() => goto(`/flows/run/${flow.path}`)}
type="button"
class="inline-flex items-center text-gray-700 bg-transparent border border-gray-700 hover:bg-gray-800 hover:text-white focus:ring-4 focus:outline-none focus:ring-gray-300 font-medium rounded-lg text-xs px-3 py-1.5 text-center"
data-dismiss-target="#alert-additional-content-5"

View File

@@ -8,9 +8,10 @@
</script>
<div
class="border p-4 rounded-sm shadow-sm space-y-2 hover:border-blue-600 flex flex-col justify-between"
class="border p-4 rounded-sm shadow-sm space-y-2 hover:border-blue-600 flex flex-col justify-between cursor-pointer"
on:click={() => goto(`/scripts/get/${script.hash}`)}
>
<div class="font-bold">{script.summary}</div>
<div class="font-bold">{script.summary || script.path}</div>
<div class="inline-flex justify-between w-full">
<div class="text-xs">{script.path}</div>
@@ -31,7 +32,7 @@
</div>
<div class="inline-flex space-x-2 space-x-reverse flex-row-reverse w-full">
<button
on:click={() => goto(`/scripts/edit/${script.hash}?step=2`)}
on:click|stopPropagation={() => goto(`/scripts/edit/${script.hash}?step=2`)}
type="button"
class="inline-flex items-center text-gray-700 bg-transparent border border-gray-700 hover:bg-gray-800 hover:text-white focus:ring-4 focus:outline-none focus:ring-gray-300 font-medium rounded-lg text-xs px-3 py-1.5 text-center"
data-dismiss-target="#alert-additional-content-5"
@@ -54,7 +55,7 @@
</button>
<button
on:click={() => goto(`/scripts/run/${script.hash}`)}
on:click|stopPropagation={() => goto(`/scripts/run/${script.hash}`)}
type="button"
class="inline-flex items-center text-gray-700 bg-transparent border border-gray-700 hover:bg-gray-800 hover:text-white focus:ring-4 focus:outline-none focus:ring-gray-300 font-medium rounded-lg text-xs px-3 py-1.5 text-center"
data-dismiss-target="#alert-additional-content-5"

View File

@@ -0,0 +1,54 @@
<script lang="ts">
import { classNames } from '$lib/utils'
import { navigating } from '$app/stores'
import type { IconDefinition } from '@fortawesome/free-solid-svg-icons'
import Icon from 'svelte-awesome'
export let label: string
export let href: string
export let icon: IconDefinition
export let isCollapsed: boolean
export let disabled: boolean = false
let isSelected = false
navigating.subscribe(() => {
if (href === '/') {
isSelected = window.location.pathname === href
} else {
isSelected = window.location.pathname.includes(href)
}
})
</script>
{#if !disabled}
<a
{href}
class={classNames(
'group flex items-center px-2 py-2 text-sm font-medium rounded-md h-8',
isSelected ? 'bg-gray-50 text-gray-900' : 'text-gray-600 hover:bg-gray-50 hover:text-gray-900'
)}
target={href.includes('http') ? '_blank' : null}
>
<Icon
data={icon}
class={classNames(
' flex-shrink-0 h-4 w-4',
isSelected ? ' text-gray-700' : 'text-white group-hover:text-gray-500',
isCollapsed ? '-mr-1' : 'mr-3'
)}
/>
{#if !isCollapsed}
<span
class={classNames(
'whitespace-pre text-white',
isSelected ? ' text-gray-700 font-bold' : 'text-white group-hover:text-gray-500'
)}
>
{label}
</span>
{/if}
</a>
{/if}

View File

@@ -0,0 +1,73 @@
<script lang="ts">
import {
faBookOpen,
faCalendar,
faCode,
faCubes,
faEye,
faHomeAlt,
faPlay,
faRobot,
faUsersCog,
faWallet,
faWind,
faCog
} from '@fortawesome/free-solid-svg-icons'
import { faDiscord, faGithub } from '@fortawesome/free-brands-svg-icons'
import MenuLink from './MenuLink.svelte'
import { userStore } from '$lib/stores'
const mainMenuLinks = [
{ label: 'Home', href: '/', icon: faHomeAlt },
{ label: 'Scripts', href: '/scripts', icon: faCode },
{ label: 'Flows', href: '/flows', icon: faWind },
{ label: 'Runs', href: '/runs', icon: faPlay },
{ label: 'Schedules', href: '/schedules', icon: faCalendar },
{ label: 'Variables', href: '/variables', icon: faWallet },
{ label: 'Resources', href: '/resources', icon: faCubes }
]
$: secondaryMenuLinks = [
{ label: 'Workers', href: '/workers', icon: faRobot },
{ label: 'Groups', href: '/groups', icon: faUsersCog },
{ label: 'Audit Logs', href: '/audit_logs', icon: faEye },
{
label: 'Workspace',
href: '/workspace_settings',
icon: faCog,
disabled: !$userStore?.is_admin
}
]
const thirdMenuLinks = [
{ label: 'Documentation', href: 'https://docs.windmill.dev/docs/intro/', icon: faBookOpen },
{ label: 'Feedbacks?', href: 'https://discord.gg/V7PM2YHsPB', icon: faDiscord },
{
label: 'Issues?',
href: 'https://github.com/windmill-labs/windmill/issues/new',
icon: faGithub
}
]
export let isCollapsed: boolean = false
</script>
<div class="flex-1 flex flex-col py-4 overflow-y-auto overflow-x-hidden">
<nav class="h-full flex justify-between flex-col px-2">
<div class="space-y-2">
{#each mainMenuLinks as menuLink}
<MenuLink {...menuLink} {isCollapsed} />
{/each}
<div class="h-8" />
{#each secondaryMenuLinks as menuLink}
<MenuLink {...menuLink} {isCollapsed} />
{/each}
</div>
<div class="space-y-2">
{#each thirdMenuLinks as menuLink}
<MenuLink {...menuLink} {isCollapsed} />
{/each}
</div>
</nav>
</div>

View File

@@ -0,0 +1,102 @@
<script lang="ts">
import { logout } from '$lib/logout'
import { userStore, usersWorkspaceStore, superadmin } from '$lib/stores'
import { classNames } from '$lib/utils'
import { faCog, faCrown, faUser } from '@fortawesome/free-solid-svg-icons'
import { onMount } from 'svelte'
import Icon from 'svelte-awesome'
import { scale } from 'svelte/transition'
let show = false
let menu: HTMLDivElement
export let isCollapsed: boolean = false
const handleOutsideClick = (event) => {
if (show && !menu.contains(event.target)) {
show = false
}
}
const handleEscape = (event) => {
if (show && event.key === 'Escape') {
show = false
}
}
onMount(() => {
document.addEventListener('click', handleOutsideClick, false)
document.addEventListener('keyup', handleEscape, false)
return () => {
document.removeEventListener('click', handleOutsideClick, false)
document.removeEventListener('keyup', handleEscape, false)
}
})
</script>
<div class="relative" bind:this={menu}>
<div>
<button
type="button"
class={classNames(
'group w-full flex items-center text-white hover:bg-gray-50 hover:text-gray-900 focus:outline-none px-2 py-2 text-sm font-medium rounded-md h-8 '
)}
on:click={() => (show = !show)}
>
<Icon
data={faUser}
class={classNames('flex-shrink-0 h-4 w-4', isCollapsed ? '-mr-1' : 'mr-2')}
/>
{#if !isCollapsed}
<span class={classNames('whitespace-pre ')}>
{$userStore?.username ?? ($superadmin ? $superadmin : '___')}
{#if $userStore?.is_admin}
<Icon data={faCrown} scale={0.6} />
{/if}
</span>
{/if}
</button>
{#if show}
<div
in:scale={{ duration: 100, start: 0.95 }}
out:scale={{ duration: 75, start: 0.95 }}
class="z-50 origin-top-left absolute left-0 mt-2 w-56 rounded-md shadow-lg bg-white ring-1 ring-black ring-opacity-5 divide-y divide-gray-100 focus:outline-none"
role="menu"
tabindex="-1"
>
<div class="px-4 py-3" role="none">
<p class="text-sm" role="none">Signed in as</p>
<p class="text-sm font-medium text-gray-900 truncate" role="none">
{$usersWorkspaceStore?.email}
</p>
</div>
<div class="py-1" role="none">
<a
href="/user/settings"
class="text-gray-700 block px-4 py-2 text-sm hover:bg-gray-100 hover:text-gray-900"
role="menuitem"
tabindex="-1"
>
Account settings
</a>
</div>
<div class="py-1" role="none">
<button
type="button"
class="text-gray-700 block w-full text-left px-4 py-2 text-sm hover:bg-gray-100 hover:text-gray-900"
role="menuitem"
tabindex="-1"
on:click={() => logout()}
>
Sign out
</button>
</div>
</div>
{/if}
</div>
</div>

View File

@@ -0,0 +1,109 @@
<script lang="ts">
import { workspaceStore, usersWorkspaceStore } from '$lib/stores'
import { classNames } from '$lib/utils'
import {
faBuilding,
faCog,
faFolder,
faFolderOpen,
faFolderTree
} from '@fortawesome/free-solid-svg-icons'
import { onMount } from 'svelte'
import Icon from 'svelte-awesome'
import { scale } from 'svelte/transition'
let show = false
let menu: HTMLDivElement
export let isCollapsed: boolean = false
const handleOutsideClick = (event) => {
if (show && !menu.contains(event.target)) {
show = false
}
}
const handleEscape = (event) => {
if (show && event.key === 'Escape') {
show = false
}
}
onMount(() => {
document.addEventListener('click', handleOutsideClick, false)
document.addEventListener('keyup', handleEscape, false)
return () => {
document.removeEventListener('click', handleOutsideClick, false)
document.removeEventListener('keyup', handleEscape, false)
}
})
</script>
<div class="relative" bind:this={menu}>
<div>
<button
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 '
)}
on:click={() => (show = !show)}
>
<Icon
data={faBuilding}
class={classNames('flex-shrink-0 h-4 w-4', isCollapsed ? '-mr-1' : 'mr-2')}
/>
{#if !isCollapsed}
<span class={classNames('whitespace-pre')}> {$workspaceStore} </span>
{/if}
</button>
{#if show}
<div
in:scale={{ duration: 100, start: 0.95 }}
out:scale={{ duration: 75, start: 0.95 }}
class="z-50 origin-top-left absolute left-0 mt-2 w-56 rounded-md shadow-lg bg-white ring-1 ring-black ring-opacity-5 divide-y divide-gray-100 focus:outline-none"
role="menu"
tabindex="-1"
>
<div class="py-1" role="none">
{#each $usersWorkspaceStore?.workspaces ?? [] as workspace}
<button
on:click={() => {
workspaceStore.set(workspace.id)
show = false
}}
class="block px-4 py-2 text-xs text-gray-500 "
role="menuitem"
tabindex="-1"
>
<span class="text-gray-300 font-mono pr-1 text-xs">{workspace.id}</span>
{workspace.name}
</button>
{/each}
<a
href="/user/create_workspace"
class="text-gray-700 block px-4 py-2 text-sm hover:bg-gray-100 hover:text-gray-900"
role="menuitem"
tabindex="-1"
>
Create new workspace
</a>
<a
href="/user/workspaces"
on:click={() => {
localStorage.removeItem('workspace')
}}
class="text-gray-700 block px-4 py-2 text-sm hover:bg-gray-100 hover:text-gray-900"
role="menuitem"
tabindex="-1"
>
See all workspaces & invites
</a>
</div>
</div>
{/if}
</div>
</div>

View File

@@ -54,8 +54,9 @@ export function displayDate(dateString: string | undefined): string {
if (date.toString() === 'Invalid Date') {
return ''
} else {
return `${date.getFullYear()}/${date.getMonth() + 1
}/${date.getDate()} at ${date.toLocaleTimeString()}`
return `${date.getFullYear()}/${
date.getMonth() + 1
}/${date.getDate()} at ${date.toLocaleTimeString()}`
}
}
@@ -556,3 +557,7 @@ export function scriptToHubUrl(
return url
}
export function classNames(...classes: string[]): string {
return classes.filter(Boolean).join(' ')
}

View File

@@ -1,361 +1,162 @@
<script lang="ts">
import { OpenAPI } from '$lib/gen'
import { logout } from '$lib/logout'
import { superadmin, userStore, usersWorkspaceStore, workspaceStore } from '$lib/stores'
import { clickOutside } from '$lib/utils'
import { faDiscord, faGithub } from '@fortawesome/free-brands-svg-icons'
import {
faBookOpen,
faCalendar,
faChevronDown,
faChevronLeft,
faChevronRight,
faCog,
faCrown,
faCubes,
faEye,
faHome,
faPlay,
faRobot,
faScroll,
faUser,
faUsersCog,
faWallet,
faWind
} from '@fortawesome/free-solid-svg-icons'
import { onMount } from 'svelte'
import { faArrowLeft } from '@fortawesome/free-solid-svg-icons'
import Icon from 'svelte-awesome'
import UserMenu from '$lib/components/sidebar/UserMenu.svelte'
import { OpenAPI } from '$lib/gen'
import { classNames } from '$lib/utils'
import WorkspaceMenu from '$lib/components/sidebar/WorkspaceMenu.svelte'
import SidebarContent from '$lib/components/sidebar/SidebarContent.svelte'
import '../app.css'
OpenAPI.WITH_CREDENTIALS = true
let menuOpen = false
let workspacePickerOpen = false
let isMobile = false
let viewportWidth = 3000
let isCollapsed = false
function openMenu(): void {
menuOpen = true
let innerWidth = window.innerWidth
function disableCollapse() {
isCollapsed = false
}
function handleClickOutside(event: any): void {
if (isMobile || viewportWidth < 640) {
isCollapsed = true
}
}
function handleClickOutsideMenu(event: any): void {
menuOpen = false
}
function handleClickOutsideWorkspacePicker(event: any): void {
workspacePickerOpen = false
}
onMount(async () => {
isMobile = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent)
//Mobile
isCollapsed = isMobile
})
$: innerWidth < 768 && disableCollapse()
</script>
<div bind:clientWidth={viewportWidth} class="h-full max-w-screen">
<nav
use:clickOutside
on:click_outside={handleClickOutside}
class="flex flex-col fixed h-screen {isCollapsed
? 'w-8'
: 'w-36'} bg-blue-500 rounded-sm text-white z-10"
>
<div class="shrink">
<button
class="w-full flex flex-row-reverse transform hover:translate-x-1 transition-transform ease-in duration-200"
on:click={() => {
isCollapsed = !isCollapsed
}}
>
<div class="pt-1 pr-3">
<Icon data={isCollapsed ? faChevronRight : faChevronLeft} scale={0.9} />
</div>
</button>
</div>
<ul class="flex flex-col {isCollapsed ? 'items-center' : 'px-6'} bg-transparent pb-1">
<li class="z-20 font-medium items-center bg-transparent">
<div
class="flex justify-center"
use:clickOutside
on:click_outside={handleClickOutsideWorkspacePicker}
>
<button
type="button"
class="flex text-sm focus:outline-none bg-transparent"
id="user-menu-button"
aria-expanded="false"
aria-haspopup="true"
>
<span class="sr-only">Open user menu</span>
<div
class="flex flex-row items-center w-full justify-content"
on:click={() => {
workspacePickerOpen = true
}}
>
<span class:hidden={isCollapsed} class="pr-2 font-mono text-xs flex"
>{$workspaceStore ?? '_______'}
<Icon class="text-white float-right mt-1 pl-1" data={faChevronDown} scale={0.6} />
</span>
<span class:hidden={!isCollapsed}>W</span>
</div>
</button>
<div
class="absolute {isCollapsed
? 'left-4'
: 'left-20'} -top-2 mt-2 w-52 rounded-md shadow-lg py-1 bg-white ring-1 ring-black ring-opacity-5 focus:outline-none {workspacePickerOpen
? 'visible'
: 'invisible'} z-40"
role="menu"
tabindex="-1"
>
{#each $usersWorkspaceStore?.workspaces ?? [] as workspace}
<button
on:click={() => {
workspaceStore.set(workspace.id)
workspacePickerOpen = false
}}
class="block px-4 py-2 text-xs text-gray-500 "
role="menuitem"
tabindex="-1"
id="user-menu-item-2"
>
<span class="text-gray-300 font-mono pr-1 text-xs">{workspace.id}</span
>{workspace.name}
</button>
{/each}
<a
href="/user/create_workspace"
class="block px-4 py-2 text-blue-600 text-left text-xs "
role="menuitem"
tabindex="-1"
id="user-menu-item-2"
>
<span class="text-gray-300 font-mono pr-1">+</span>Create new workspace</a
>
<a
href="/user/workspaces"
class="block px-4 py-2 text-blue-600 text-left text-xs "
role="menuitem"
tabindex="-1"
id="user-menu-item-2"
on:click={() => {
localStorage.removeItem('workspace')
}}
>
See all workspaces & invites</a
>
</div>
</div>
</li>
</ul>
<ul class="flex flex-col max-h-12 mt-2 {isCollapsed ? 'items-center' : 'px-6 '} ">
<li class="relative z-30 font-medium">
<div
class="flex justify-center"
use:clickOutside
on:click_outside={handleClickOutsideMenu}
on:click={openMenu}
>
<button
type="button"
class="flex text-sm rounded-full focus:outline-none"
id="user-menu-button"
aria-expanded="false"
aria-haspopup="true"
>
<span class="sr-only">Open user menu</span>
<div class="mx-auto">
<span class:hidden={isCollapsed} class="px-2 font-mono text-xs whitespace-nowrap">
<Icon class="text-white" data={faUser} scale={0.6} />
{$userStore?.username ?? ($superadmin ? $superadmin : '___')}
{#if $userStore?.is_admin}
<Icon class="text-white" data={faCrown} scale={0.6} />
{/if}
<Icon class="inline text-white mt-1 ml-1" data={faChevronDown} scale={0.6} />
</span>
</div>
</button>
<div
class="absolute {isCollapsed
? 'left-4'
: 'left-20'} -top-5 mt-2 w-48 rounded-md shadow-lg py-1 bg-white ring-1 ring-black ring-opacity-5 focus:outline-none {menuOpen
? 'visible'
: 'invisible'}"
role="menu"
tabindex="-1"
>
<span class="block px-4 py-2 text-sm text-gray-500">{$usersWorkspaceStore?.email}</span>
<a
href="/user/settings"
class="block px-4 py-2 text-sm text-gray-700"
role="menuitem"
tabindex="-1"
id="user-menu-item-1">User settings</a
>
<button
on:click={() => logout()}
class="block px-4 py-2 text-sm text-gray-700"
role="menuitem"
tabindex="-1"
id="user-menu-item-2">Sign out</button
>
</div>
</div>
</li>
<div class="border-t border-gray-300 border-opacity-30 my-2" />
</ul>
<div class="grow h-full" />
<ul class="flex flex-col {isCollapsed ? 'items-center' : 'px-6'} ">
<li>
<a href="/" class="menu-link text-sm font-medium items-center ">
<Icon class="text-white" data={faHome} scale={0.9} />
<span class=" pl-2 {isCollapsed ? 'hidden' : ''}">Home</span>
</a>
</li>
<li>
<a href="/scripts" class="menu-link text-sm font-medium items-center ">
<Icon class="text-white" data={faScroll} scale={0.9} />
<span class=" pl-2 {isCollapsed ? 'hidden' : ''}">Scripts</span>
</a>
</li>
<li>
<a href="/flows" class="menu-link text-sm font-medium items-center">
<Icon class="text-white" data={faWind} scale={0.9} />
<span class="pl-2 {isCollapsed ? 'hidden' : ''}">Flows</span>
</a>
</li>
<li>
<a href="/runs" class="menu-link text-sm font-medium items-center">
<Icon class="text-white" data={faPlay} scale={0.8} />
<span class=" pl-2 {isCollapsed ? 'hidden' : ''}">Runs</span>
</a>
</li>
<li>
<a href="/schedules" class="menu-link text-sm font-medium items-center">
<Icon class="text-white" data={faCalendar} scale={0.9} />
<span class=" pl-2 {isCollapsed ? 'hidden' : ''}">Schedules</span>
</a>
</li>
<li>
<a href="/variables" class="menu-link text-sm font-medium items-center">
<Icon class="text-white" data={faWallet} scale={0.9} />
<span class=" pl-2 {isCollapsed ? 'hidden' : ''}">Variables</span>
</a>
</li>
<li>
<a href="/resources" class="menu-link text-sm font-medium items-center">
<Icon class="text-white" data={faCubes} scale={0.9} />
<span class=" pl-2 {isCollapsed ? 'hidden' : ''}">Resources</span>
</a>
</li>
</ul>
<svelte:window bind:innerWidth />
<div class="grow h-full" />
<ul class="flex flex-col bg-blue {isCollapsed ? 'items-center' : 'px-6'} ">
<div class="border-t border-gray-300 border-opacity-30" />
{#if $superadmin}
<li>
<a href="/workers" class="menu-link text-sm font-medium items-center">
<Icon class="text-white" data={faRobot} scale={0.9} />
<span class=" pl-2 {isCollapsed ? 'hidden' : ''}">Workers</span>
</a>
</li>
{/if}
<li>
<a href="/groups" class="menu-link text-sm font-medium items-center">
<Icon class="text-white" data={faUsersCog} scale={0.9} />
<span class="pl-2 t {isCollapsed ? 'hidden' : ''}">Groups</span>
</a>
</li>
<li>
<a href="/audit_logs" class="menu-link text-sm font-medium items-center">
<Icon class="text-white" data={faEye} scale={0.9} />
<span class=" pl-2 {isCollapsed ? 'hidden' : ''}">Audit logs</span>
</a>
</li>
{#if $userStore?.is_admin}
<li>
<a href="/workspace_settings" class="menu-link text-sm font-medium items-center pb-2">
<Icon class="text-white" data={faCog} scale={0.9} />
<span class=" pl-2 {isCollapsed ? 'hidden' : ''}">Workspace</span>
</a>
</li>
{/if}
<div class="border-t border-gray-300 border-opacity-30" />
</ul>
<div class="grow h-full" />
<ul class="flex flex-col {isCollapsed ? 'items-center' : 'px-6'} block">
<li class="">
<a
href="https://github.com/windmill-labs/windmill/issues/new"
class="menu-link text-sm font-medium items-center"
target="_blank"
<div>
<div
class={classNames('relative md:hidden ', menuOpen ? 'z-40' : 'pointer-events-none')}
role="dialog"
aria-modal="true"
>
<div
class={classNames(
'fixed inset-0 bg-gray-600 bg-opacity-75 transition-opacity ease-linear duration-300',
menuOpen ? 'opacity-100' : 'opacity-0 '
)}
/>
<div class="fixed inset-0 flex">
<div
class={classNames(
'relative flex-1 flex flex-col max-w-xs w-full bg-white transition ease-in-out duration-300 transform',
menuOpen ? 'translate-x-0' : '-translate-x-full'
)}
>
<div
class={classNames(
'absolute top-0 right-0 -mr-12 pt-2 ease-in-out duration-300',
menuOpen ? 'opacity-100' : 'opacity-0'
)}
>
<Icon class="text-white" data={faGithub} scale={0.9} />
<span class="pl-2 {isCollapsed ? 'hidden' : ''}">Issue?</span>
</a>
</li>
<li class="">
<a
href="https://discord.gg/V7PM2YHsPB"
class="menu-link text-sm font-medium items-center"
target="_blank"
>
<Icon class="text-white" data={faDiscord} scale={0.9} />
<span class="pl-2 {isCollapsed ? 'hidden' : ''}">Feedback</span>
</a>
</li>
<li class="">
<a
href="https://docs.windmill.dev/docs/intro"
class="menu-link text-sm font-medium items-center"
target="_blank"
>
<Icon class="text-white" data={faBookOpen} scale={0.9} />
<span class="pl-2 {isCollapsed ? 'hidden' : ''}">Docs</span>
</a>
</li>
<li class="">
<button
type="button"
on:click={() => {
menuOpen = !menuOpen
}}
class="ml-1 flex items-center justify-center h-10 w-10 rounded-full focus:outline-none focus:ring-2 focus:ring-inset focus:ring-white"
>
<svg
class="h-6 w-6 text-white"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="2"
stroke="currentColor"
aria-hidden="true"
>
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</div>
<div class="bg-blue-500 h-full">
<div class="flex items-center flex-shrink-0 p-4 font-extrabold text-white">Windmill</div>
<div class="px-2 py-4 space-y-2 border-y border-blue-400 ">
<WorkspaceMenu />
<UserMenu />
</div>
<SidebarContent {isCollapsed} />
</div>
</div>
</div>
</div>
<div
class={classNames(
'hidden md:flex md:flex-col md:fixed md:inset-y-0 transition-all ease-in-out duration-200 ',
isCollapsed ? 'md:w-12' : 'md:w-48'
)}
>
<div class="flex-1 flex flex-col min-h-0 shadow-lg bg-blue-500">
<div class="flex items-center flex-shrink-0 p-4 font-extrabold text-white">
{#if isCollapsed}
W
{:else}
Windmill
{/if}
</div>
<div class="px-2 py-4 space-y-2 border-y border-blue-400 ">
<WorkspaceMenu {isCollapsed} />
<UserMenu {isCollapsed} />
</div>
<SidebarContent {isCollapsed} />
<div class="flex-shrink-0 flex p-4 border-t border-blue-400">
<button
class="h-12 flex flex-row text-sm font-medium min-w-full px-5 items-center transform hover:translate-x-1 transition-transform ease-in duration-200"
on:click={() => {
isCollapsed = !isCollapsed
}}
>
<div class="w-full -ml-4">
<div class="flex flex-row justify-between w-full">
<div class="px-3 {isCollapsed ? 'hidden' : ''}">Windmill</div>
<div class="ml-4">
<Icon data={isCollapsed ? faChevronRight : faChevronLeft} scale={0.9} />
</div>
</div>
</div>
<Icon
data={faArrowLeft}
class={classNames(
'flex-shrink-0 h-4 w-4 transition-all ease-in-out duration-200 text-white',
isCollapsed ? 'rotate-180' : 'rotate-0'
)}
/>
</button>
</li>
</ul>
</nav>
</div>
</div>
</div>
<div
class="bg-white antialiased text-gray-900 {isCollapsed
? 'pl-10'
: 'pl-36 ml-2'} flex h-full max-w-screen flex-col items-center"
class={classNames(
'flex flex-col flex-1 transition-all ease-in-out duration-200',
isCollapsed ? 'md:pl-12' : 'md:pl-48'
)}
>
<slot />
<main>
<div class="border-b w-full">
<div
class="py-2 px-2 sm:px-4 md:px-8 flex justify-between items-center shadow-sm max-w-6xl mx-auto md:hidden"
>
<button
type="button"
on:click={() => {
menuOpen = true
}}
class="h-8 w-8 inline-flex items-center justify-center rounded-md text-gray-500 hover:text-gray-900 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-indigo-500"
>
<svg
class="h-6 w-6"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="2"
stroke="currentColor"
aria-hidden="true"
>
<path stroke-linecap="round" stroke-linejoin="round" d="M4 6h16M4 12h16M4 18h16" />
</svg>
</button>
</div>
<slot />
</div>
</main>
</div>
</div>
<style>
:global[menu] {
@apply text-white;
}
.menu-link {
@apply flex flex-row h-10 transform hover:translate-x-1 transition-transform ease-in duration-200 text-gray-200 hover:text-white;
}
</style>

View File

@@ -8,6 +8,8 @@
<script lang="ts">
import { page } from '$app/stores'
import CenteredModal from '$lib/components/CenteredModal.svelte'
import CenteredPage from '$lib/components/CenteredPage.svelte'
import FlowBuilder from '$lib/components/FlowBuilder.svelte'
import { initFlow } from '$lib/components/flows/flowStore'
@@ -57,4 +59,6 @@
loadFlow()
</script>
<FlowBuilder />
<CenteredPage>
<FlowBuilder />
</CenteredPage>

View File

@@ -14,7 +14,7 @@
import { workspaceStore } from '$lib/stores'
import { decodeState, emptySchema } from '$lib/utils'
import { initFlow } from '$lib/components/flows/flowStore'
import { initFlowState } from '$lib/components/flows/flowState'
import CenteredPage from '$lib/components/CenteredPage.svelte'
const initialState = $page.url.searchParams.get('state')
let flowLoadedFromUrl = initialState != undefined ? decodeState(initialState) : undefined
@@ -54,4 +54,6 @@
}
</script>
<FlowBuilder {initialPath} />
<CenteredPage>
<FlowBuilder {initialPath} />
</CenteredPage>

View File

@@ -42,14 +42,12 @@
}
}
const ressources = []
const resources = []
</script>
<CenteredPage>
<div class="space-y-12 mx-8 my-16">
<h1 class="text-xl font-extrabold tracking-tight leading-none text-gray-900 md:text-3xl">
Welcome to Windmill!
</h1>
<h1 class="text-xl font-extrabold tracking-tight leading-none text-gray-900 md:text-3xl">Home</h1>
<div class="space-y-12">
<div>
<h2
class="mb-4 text-lg font-extrabold tracking-tight leading-none text-gray-900 md:text-2xl border-b py-2"
@@ -127,11 +125,11 @@
class="mb-4 text-lg font-extrabold tracking-tight leading-none text-gray-900 md:text-2xl border-b py-2 "
>
<span class="text-transparent bg-clip-text bg-gradient-to-r to-blue-500 from-blue-600">
Ressources
Resources
</span>
</h2>
{#if ressources.length === 0}
{#if resources.length === 0}
<RessourceGettingStarted />
{/if}
</div>

View File

@@ -14,6 +14,7 @@
import ScriptBuilder from '$lib/components/ScriptBuilder.svelte'
import type { Schema } from '$lib/common'
import { decodeState, emptySchema, getScriptByPath, sendUserToast } from '$lib/utils'
import CenteredPage from '$lib/components/CenteredPage.svelte'
// Default
let schema: Schema = emptySchema()
@@ -76,4 +77,6 @@
}
</script>
<ScriptBuilder {script} />
<CenteredPage>
<ScriptBuilder {script} />
</CenteredPage>

View File

@@ -13,6 +13,7 @@
import { workspaceStore } from '$lib/stores'
import ScriptBuilder from '$lib/components/ScriptBuilder.svelte'
import { decodeState } from '$lib/utils'
import CenteredPage from '$lib/components/CenteredPage.svelte'
const initialState = $page.url.searchParams.get('state')
let scriptLoadedFromUrl = initialState != undefined ? decodeState(initialState) : undefined
@@ -40,5 +41,7 @@
</script>
{#if script}
<ScriptBuilder {initialPath} {script} />
<CenteredPage>
<ScriptBuilder {initialPath} {script} />
</CenteredPage>
{/if}