Hub lists (#989)

* fix(frontend): Adapt hub list design

* fix(frontend): revert changes
This commit is contained in:
Faton Ramadani
2022-12-04 18:06:57 +01:00
committed by GitHub
parent b64d5a621b
commit 11ef60fe9c
12 changed files with 186 additions and 210 deletions

View File

@@ -1,4 +1,4 @@
<div class="pb-6 h-fit-content">
<div class="pb-8 h-fit-content">
<div class="max-w-6xl mx-auto px-4 sm:px-6 md:px-8 h-fit-content">
<slot />
</div>

View File

@@ -7,7 +7,6 @@
import { getScriptByPath, sendUserToast } from '$lib/utils'
import {
faBuilding,
faCube,
faDollarSign,
faEye,
@@ -82,12 +81,12 @@
<DrawerContent title="Code" on:close={scriptPicker.closeDrawer}>
{#if pick_existing == 'hub'}
<PickHubScript bind:filter {kind} on:pick={onScriptPick}>
<ToggleHubWorkspace bind:selected={pick_existing} /></PickHubScript
>
<ToggleHubWorkspace bind:selected={pick_existing} />
</PickHubScript>
{:else}
<WorkspaceScriptPicker bind:filter {kind} on:pick={onScriptPick}
><ToggleHubWorkspace bind:selected={pick_existing} /></WorkspaceScriptPicker
>
<WorkspaceScriptPicker bind:filter {kind} on:pick={onScriptPick}>
<ToggleHubWorkspace bind:selected={pick_existing} />
</WorkspaceScriptPicker>
{/if}
</DrawerContent>
</Drawer>

View File

@@ -1,39 +1,41 @@
<script lang="ts">
import { faClose } from '@fortawesome/free-solid-svg-icons'
import { createEventDispatcher } from 'svelte'
import Icon from 'svelte-awesome'
import Button from '../button/Button.svelte'
export let title: string | undefined = undefined
export let overflow_y = true
export let noPadding = false
export let forceOverflowVisible = false
const dispatch = createEventDispatcher()
</script>
<div class="flex flex-col divide-y h-screen max-h-screen">
<div class="flex justify-between items-center gap-x-2">
<Button
btnClasses="m-1"
variant="border"
size="lg"
color="dark"
on:click={() => {
dispatch('close')
}}
>
<Icon data={faClose} />
</Button>
<span class="font-bold truncate">{title}</span>
<div class="flex flex-row m-1">
{#if $$slots.submission}
<slot name="submission" class="sticky" />
{/if}
<div class="flex justify-between w-wull items-center px-4 py-2">
<div class="flex items-center gap-2">
<Button
size="lg"
color="light"
on:click={() => dispatch('close')}
startIcon={{ icon: faClose }}
iconOnly
/>
<span class="font-semibold truncate text-gray-800">{title}</span>
</div>
<div>
<div class="flex gap-2 items-center">
{#if $$slots.submission}
<slot name="submission" class="sticky" />
{/if}
</div>
</div>
</div>
<div
class="{noPadding ? '' : 'py-2 px-6'} grow h-full max-h-full {forceOverflowVisible
class="{noPadding ? '' : 'p-4'} grow h-full max-h-full {forceOverflowVisible
? '!overflow-visible'
: ''}"
class:overflow-y-auto={overflow_y}

View File

@@ -127,12 +127,12 @@
>Use pre-made <span class="text-blue-500">{kind == 'script' ? 'common' : kind}</span> script</h3
>
{#if pick_existing == 'hub'}
<PickHubScript bind:filter {kind} on:pick
><ToggleHubWorkspace bind:selected={pick_existing} /></PickHubScript
>
<PickHubScript bind:filter {kind} on:pick>
<ToggleHubWorkspace bind:selected={pick_existing} />
</PickHubScript>
{:else}
<WorkspaceScriptPicker bind:filter {kind} on:pick
><ToggleHubWorkspace bind:selected={pick_existing} /></WorkspaceScriptPicker
>
<WorkspaceScriptPicker bind:filter {kind} on:pick>
<ToggleHubWorkspace bind:selected={pick_existing} />
</WorkspaceScriptPicker>
{/if}
</div>

View File

@@ -2,25 +2,25 @@
import { hubScripts } from '$lib/stores'
import { createEventDispatcher, onMount } from 'svelte'
import type { HubItem } from './model'
import IconedResourceType from '$lib/components/IconedResourceType.svelte'
import { Badge, Skeleton } from '$lib/components/common'
import SearchItems from '$lib/components/SearchItems.svelte'
import { loadHubScripts } from '$lib/utils'
import { capitalize, classNames, loadHubScripts } from '$lib/utils'
import NoItemFound from '$lib/components/home/NoItemFound.svelte'
import { APP_TO_ICON_COMPONENT } from '$lib/components/icons'
import ListFilters from '$lib/components/home/ListFilters.svelte'
export let kind: 'script' | 'trigger' | 'approval' | 'failure' = 'script'
export let filter = ''
$: items = ($hubScripts ?? []).filter((i) => i.kind === kind)
const dispatch = createEventDispatcher()
let filteredItems: (HubItem & { marked?: string })[] = []
let appFilter: string | undefined = undefined
$: items = ($hubScripts ?? []).filter((i) => i.kind === kind)
$: prefilteredItems = appFilter ? (items ?? []).filter((i) => i.app == appFilter) : items ?? []
$: apps = Array.from(new Set(filteredItems?.map((x) => x.app) ?? [])).sort()
const dispatch = createEventDispatcher()
onMount(() => {
if (!$hubScripts) {
loadHubScripts()
@@ -29,60 +29,60 @@
</script>
<SearchItems {filter} items={prefilteredItems} bind:filteredItems f={(x) => x.summary} />
<div class="flex flex-col min-h-0">
<div class="w-full flex mt-1 items-center gap-2 mb-5">
<slot />
<input type="text" placeholder="Search Hub Scripts" bind:value={filter} class="text-2xl grow" />
</div>
<div class="gap-2 w-full flex flex-wrap pb-3">
{#each apps as app}
<Badge
class="cursor-pointer hover:bg-gray-200 border"
on:click={() => {
appFilter = appFilter == app ? undefined : app
}}
capitalize
color={app === appFilter ? 'blue' : 'gray'}
>
{app}
{#if app === appFilter}&cross;{/if}
</Badge>
{/each}
</div>
<div class="overflow-auto">
<ul class="divide-y divide-gray-200">
{#if $hubScripts}
{#if filter.length > 0 && filteredItems.length == 0}
<p>No items found</p>
{/if}
{#each filteredItems as obj}
<li class="flex flex-row w-full">
<button
class="py-4 px-1 gap-1 flex flex-row grow hover:bg-blue-50 bg-white transition-all"
on:click={() => {
dispatch('pick', obj)
}}
>
<div class="mr-2 text-sm text-left truncate w-32 shrink-0">
<IconedResourceType after={true} silent={false} name={obj['app']} />
</div>
<div class="mr-2 text-left">
{#if obj.marked}
{@html obj.marked ?? ''}
{:else}
{obj.summary ?? ''}
{/if}
</div>
</button>
</li>
{/each}
{:else}
{#each Array(10).fill(0) as sk}
<Skeleton layout={[[4], 0.5]} />
{/each}
{/if}
</ul>
</div>
<div class="w-full flex mt-1 items-center gap-2 mb-5">
<slot />
<input type="text" placeholder="Search Hub Scripts" bind:value={filter} class="text-2xl grow" />
</div>
<ListFilters filters={apps} bind:selectedFilter={appFilter} />
{#if $hubScripts}
{#if filteredItems.length == 0}
<NoItemFound />
{:else}
<ul class="divide-y divide-gray-200 border rounded-md overflow-hidden">
{#each filteredItems as item (item.path)}
<li class="flex flex-row w-full">
<button
class="p-4 gap-4 flex flex-row grow hover:bg-gray-50 bg-white transition-all items-center"
on:click={() => dispatch('pick', item)}
>
<div class="flex items-center gap-4">
<div
class={classNames(
'rounded-md p-1 flex justify-center items-center border',
'bg-gray-50 border-gray-200'
)}
>
<svelte:component
this={APP_TO_ICON_COMPONENT[item['app']]}
height={18}
width={18}
/>
</div>
<div class="w-full text-left font-normal ">
<div class="text-gray-900 flex-wrap text-md font-semibold mb-1">
{#if item.marked}
{@html item.marked ?? ''}
{:else}
{item.summary ?? ''}
{/if}
</div>
<div class="text-gray-600 text-xs ">
{item.path}
</div>
</div>
</div>
{#if kind !== 'script'}
<Badge color="gray" baseClass="border">{capitalize(kind)}</Badge>
{/if}
</button>
</li>
{/each}
</ul>
{/if}
{:else}
{#each Array(10).fill(0) as _}
<Skeleton layout={[[4], 0.5]} />
{/each}
{/if}

View File

@@ -3,8 +3,6 @@
import {
AppService,
FlowService,
Job,
JobService,
ListableApp,
Script,
ScriptService,
@@ -13,22 +11,10 @@
} from '$lib/gen'
import { superadmin, userStore, workspaceStore } from '$lib/stores'
import VirtualList from '@sveltejs/svelte-virtual-list'
import {
Button,
Drawer,
DrawerContent,
Skeleton,
ToggleButton,
ToggleButtonGroup
} from '$lib/components/common'
import { Drawer, Skeleton, ToggleButton, ToggleButtonGroup } from '$lib/components/common'
import { canWrite, classNames, pluralize } from '$lib/utils'
import type { HubItem } from '$lib/components/flows/pickers/model'
import ShareModal from '$lib/components/ShareModal.svelte'
import { faCodeFork, faGlobe } from '@fortawesome/free-solid-svg-icons'
import FlowViewer from '$lib/components/FlowViewer.svelte'
import HighlightCode from '$lib/components/HighlightCode.svelte'
import SearchItems from '$lib/components/SearchItems.svelte'
import Badge from '$lib/components/common/badge/Badge.svelte'
import type uFuzzy from '@leeoniya/ufuzzy'
import { Code2, LayoutDashboard, Wind } from 'svelte-lucide'
@@ -36,10 +22,9 @@
import FlowRow from '$lib/components/common/table/FlowRow.svelte'
import AppRow from '$lib/components/common/table/AppRow.svelte'
import { fade } from 'svelte/transition'
import { flip } from 'svelte/animate'
let jobs: Job[] = []
import NoItemFound from './NoItemFound.svelte'
import ListFilters from './ListFilters.svelte'
import SearchItems from '../SearchItems.svelte'
type TableItem<T, U extends 'script' | 'flow' | 'app'> = T & {
canWrite: boolean
@@ -66,13 +51,6 @@
let shareModalFlows: ShareModal
let loading = true
let flowViewer: Drawer
let flowViewerFlow: { flow?: OpenFlow & { id?: number } } | undefined
let codeViewer: Drawer
let codeViewerContent: string = ''
let codeViewerLanguage: 'deno' | 'python3' | 'go' | 'bash' = 'deno'
let codeViewerObj: HubItem | undefined = undefined
async function loadScripts(): Promise<void> {
const loadedScripts = await ScriptService.listScripts({
@@ -152,24 +130,14 @@
let ownerFilter: string | undefined = undefined
async function loadJobs() {
jobs = await JobService.listJobs({
workspace: $workspaceStore!,
success: true,
createdBy: $userStore?.username,
jobKinds: 'flow,script'
})
loading = false
}
$: {
if (($userStore || $superadmin) && $workspaceStore) {
loadScripts()
loadFlows()
loadApps()
loadJobs()
}
}
const cmp = new Intl.Collator('en').compare
const opts: uFuzzy.Options = {
@@ -258,50 +226,6 @@
}}
/>
<Drawer bind:this={codeViewer} size="900px">
<DrawerContent title={codeViewerObj?.summary ?? ''} on:close={codeViewer.closeDrawer}>
<div slot="submission" class="flex flex-row gap-2 pr-2">
<Button
href="https://hub.windmill.dev/scripts/{codeViewerObj?.app ?? ''}/{codeViewerObj?.ask_id ??
0}"
startIcon={{ icon: faGlobe }}
variant="border"
>
View on the Hub
</Button>
<Button
href="/scripts/add?hub={encodeURIComponent(codeViewerObj?.path ?? '')}"
startIcon={{ icon: faCodeFork }}
>
Fork
</Button>
</div>
<HighlightCode language={codeViewerLanguage} code={codeViewerContent} />
</DrawerContent>
</Drawer>
<Drawer bind:this={flowViewer} size="900px">
<DrawerContent title="Hub flow" on:close={flowViewer.closeDrawer}>
<div slot="submission" class="flex flex-row gap-2 pr-2">
<Button
href="https://hub.windmill.dev/flows/{flowViewerFlow?.flow?.id}"
startIcon={{ icon: faGlobe }}
variant="border"
>
View on the Hub
</Button>
<Button href="/flows/add?hub={flowViewerFlow?.flow?.id}" startIcon={{ icon: faCodeFork }}>
Fork
</Button>
</div>
{#if flowViewerFlow?.flow}
<FlowViewer flow={flowViewerFlow.flow} />
{/if}
</DrawerContent>
</Drawer>
<CenteredPage>
<div class="flex flex-col md:flex-row gap-2 items-center sm:justify-between w-full">
<div>
@@ -358,25 +282,8 @@
</button>
</div>
</div>
{#if owners.length > 0}
<div class="gap-2 w-full flex flex-wrap my-4">
{#each owners as owner (owner)}
<div in:fade={{ duration: 50 }} animate:flip={{ duration: 100 }}>
<Badge
class="cursor-pointer hover:bg-gray-200"
on:click={() => {
ownerFilter = ownerFilter == owner ? undefined : owner
}}
color={owner === ownerFilter ? 'blue' : 'gray'}
baseClass={owner === ownerFilter ? 'border border-blue-500' : 'border'}
>
{owner}
{#if owner === ownerFilter}&cross;{/if}
</Badge>
</div>
{/each}
</div>
{/if}
<ListFilters bind:selectedFilter={ownerFilter} filters={owners} />
<div>
{#if filteredItems == undefined}
<div class="mt-4" />
@@ -385,12 +292,7 @@
<Skeleton layout={[[4], 0.5]} />
{/each}
{:else if filteredItems.length === 0}
<div class="flex justify-center items-center h-48">
<div class="text-gray-500 text-center">
<div class="text-2xl font-bold">No items found</div>
<div class="text-sm">Try changing your search or filters</div>
</div>
</div>
<NoItemFound />
{:else}
<div
class={classNames('border rounded-md')}

View File

@@ -0,0 +1,32 @@
<script lang="ts">
import { classNames } from '$lib/utils'
import { flip } from 'svelte/animate'
import { fade } from 'svelte/transition'
import { Badge } from '../common'
export let filters: string[]
export let selectedFilter: string | undefined = undefined
</script>
{#if Array.isArray(filters) && filters.length > 0}
<div class="gap-2 w-full flex flex-wrap my-4">
{#each filters as filter (filter)}
<div in:fade={{ duration: 50 }} animate:flip={{ duration: 100 }}>
<Badge
class={classNames(
'cursor-pointer',
filter === selectedFilter ? 'hover:bg-blue-200' : 'hover:bg-gray-200'
)}
on:click={() => {
selectedFilter = selectedFilter == filter ? undefined : filter
}}
color={filter === selectedFilter ? 'blue' : 'gray'}
baseClass={filter === selectedFilter ? 'border border-blue-500' : 'border'}
>
{filter}
{#if filter === selectedFilter}&cross;{/if}
</Badge>
</div>
{/each}
</div>
{/if}

View File

@@ -0,0 +1,6 @@
<div class="flex justify-center items-center h-48">
<div class="text-gray-500 text-center">
<div class="text-2xl font-bold">No items found</div>
<div class="text-sm">Try changing your search or filters</div>
</div>
</div>

View File

@@ -0,0 +1,8 @@
<script lang="ts">
import { QrCode } from 'svelte-lucide'
export let height = '24px'
export let width = '24px'
</script>
<QrCode size={height} />

View File

@@ -0,0 +1,16 @@
<script lang="ts">
export let height = '24px'
export let width = '24px'
</script>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 1565 1559"
{height}
{width}
style="fill:#000000"
>
<path
d="M522.63 807.57C515.63 830.07 500.44 850.06 478.07 862.84L146.72 1052.69C99.92 1079.39 40.3 1063.56 13.44 1017.38C-13.59 970.94 2.32 911.86 49.08 885.08L234.26 779.17L49.08 672.98C2.32 646.24 -13.67 587.15 13.44 540.88C40.3 494.58 99.92 478.7 146.72 505.4L478.07 695.13C501.37 708.46 516.94 729.6 523.48 753.24C525.68 760.84 526.66 768.44 527.04 776.08C527.3 786.6 525.94 797.26 522.63 807.57ZM612.54 985.38C663 988.98 702.84 1030.84 702.84 1081.77L702.84 1461.36C702.84 1514.89 659.05 1558.22 605.29 1558.22C551.27 1558.22 507.65 1514.89 507.65 1461.36L507.65 1249.05L321.84 1355.29C275.25 1382.2 215.67 1366.28 188.6 1320.06C161.7 1273.75 177.61 1214.37 224.29 1187.63L555.9 998.02C573.72 987.71 593.49 983.76 612.54 985.38ZM952.17 572.85C901.59 569.15 861.83 527.22 861.83 476.33L861.83 96.69C861.83 43.34 905.37 0 959.43 0C1013.44 0 1057.07 43.34 1057.07 96.69L1057.07 309.13L1242.75 202.72C1289.43 175.98 1349.04 191.85 1375.95 238.08C1402.8 284.56 1386.98 343.68 1340.26 370.42L1008.69 560.2C990.87 570.38 971.14 574.37 952.17 572.85ZM224.29 370.42C177.61 343.68 161.7 284.56 188.6 238.08C215.67 191.85 275.25 175.98 321.84 202.72L507.65 309.13L507.65 96.69C507.65 43.34 551.27 0 605.29 0C659.05 0 702.84 43.34 702.84 96.69L702.84 476.33C702.84 527.22 663 569.15 612.54 572.85C593.49 574.37 573.72 570.38 555.9 560.2L224.29 370.42ZM819.83 952.74C815.88 956.77 808.28 960.04 802.51 960.04L761.99 960.04C756.43 960.04 748.71 956.77 744.68 952.74L606.94 816.27C603 812.41 599.81 804.68 599.81 799.25L599.81 758.97C599.81 753.41 603 745.69 606.94 741.78L744.68 605.27C748.71 601.29 756.43 598.1 761.99 598.1L802.51 598.1C808.16 598.1 815.88 601.29 819.83 605.27L957.65 741.78C961.59 745.69 964.77 753.41 964.77 758.97L964.77 799.25C964.77 804.68 961.59 812.41 957.65 816.27L819.83 952.74ZM847.49 778.37C847.49 772.85 844.14 765.17 840.19 761.09L800.26 721.7C796.36 717.8 788.6 714.57 783.04 714.57L781.47 714.57C775.91 714.57 768.19 717.8 764.32 721.7L724.4 761.09C720.41 765.17 717.35 772.85 717.35 778.37L717.35 779.94C717.35 785.37 720.41 792.97 724.4 796.96L764.32 836.43C768.23 840.34 775.91 843.56 781.47 843.56L783.04 843.56C788.6 843.56 796.36 840.34 800.26 836.43L840.19 796.96C844.14 792.97 847.49 785.37 847.49 779.94L847.49 778.37ZM1340.26 1187.63C1386.98 1214.37 1402.8 1273.75 1375.95 1320.06C1349.04 1366.28 1289.38 1382.2 1242.75 1355.29L1057.07 1249.05L1057.07 1461.36C1057.07 1514.89 1013.44 1558.22 959.43 1558.22C905.37 1558.22 861.83 1514.89 861.83 1461.36L861.83 1081.77C861.83 1030.84 901.59 988.98 952.17 985.38C971.14 983.76 990.87 987.71 1008.69 998.02L1340.26 1187.63ZM1330.37 779.17L1515.55 885.08C1562.31 911.86 1578.31 970.94 1551.28 1017.38C1524.29 1063.56 1464.59 1079.39 1418 1052.69L1086.39 862.84C1064.24 850.06 1048.92 830.07 1042.09 807.57C1038.86 797.26 1037.33 786.6 1037.72 776.08C1037.89 768.44 1039.03 760.84 1041.15 753.24C1047.77 729.6 1063.35 708.46 1086.39 695.13L1418 505.4C1464.59 478.7 1524.29 494.58 1551.28 540.88C1578.31 587.15 1562.31 646.24 1515.55 672.98L1330.37 779.17Z"
/>
</svg>

View File

@@ -20,8 +20,10 @@ import TogglIcon from './TogglIcon.svelte'
import WindmillIcon from './WindmillIcon.svelte'
import MailchimpIcon from './MailchimpIcon.svelte'
import SendgridIcon from './SendgridIcon.svelte'
import SendflakeIcon from './SendflakeIcon.svelte'
import QRCodeIcon from './QRCodeIcon.svelte'
export const APP_TO_ICON_COMPONENT = {
export const APP_TO_ICON_COMPONENT = {
postgresql: PostgresIcon,
mysql: Mysql,
smtp: Mail,
@@ -45,6 +47,8 @@ export const APP_TO_ICON_COMPONENT = {
http: HttpIcon,
mailchimp: MailchimpIcon,
sendgrid: SendgridIcon,
snowflake: SendflakeIcon,
qrcode: QRCodeIcon
} as const
export {
@@ -69,5 +73,5 @@ export {
TogglIcon,
WindmillIcon,
MailchimpIcon,
SendgridIcon,
}
SendgridIcon
}

View File

@@ -50,18 +50,24 @@
<Drawer bind:this={codeViewer} size="900px">
<DrawerContent title={codeViewerObj?.summary ?? ''} on:close={codeViewer.closeDrawer}>
<div slot="submission" class="flex flex-row gap-2 pr-2">
<div slot="submission">
<Button
href="https://hub.windmill.dev/scripts/{codeViewerObj?.app ?? ''}/{codeViewerObj?.ask_id ??
0}"
startIcon={{ icon: faGlobe }}
variant="border"
variant="contained"
color="light"
size="sm"
>
View on the Hub
<div class="flex gap-2 items-center my-1">
<Globe2 size="18px" />
View on the Hub
</div>
</Button>
<Button
href="/scripts/add?hub={encodeURIComponent(codeViewerObj?.path ?? '')}"
startIcon={{ icon: faCodeFork }}
color="dark"
size="sm"
>
Fork
</Button>
@@ -72,7 +78,7 @@
</Drawer>
<Drawer bind:this={flowViewer} size="900px">
<DrawerContent title="Hub flow" on:close={flowViewer.closeDrawer}>
<DrawerContent title="Hub flow" on:close={flowViewer.closeDrawer} noPadding>
<div slot="submission" class="flex flex-row gap-2 pr-2">
<Button
href="https://hub.windmill.dev/flows/{flowViewerFlow?.flow?.id}"
@@ -87,7 +93,9 @@
</div>
{#if flowViewerFlow?.flow}
<FlowViewer flow={flowViewerFlow.flow} />
<div class="p-4">
<FlowViewer flow={flowViewerFlow.flow} />
</div>
{/if}
</DrawerContent>
</Drawer>
@@ -114,7 +122,6 @@
</div>
</PageHeader>
<div class="my-6" />
<Tabs bind:selected={tab}>
<Tab size="md" value="workspace">
<div class="flex gap-2 items-center my-1">