feat(frontend): Rework variable table (#2576)

* feat(frontend): updated variable table

* feat(frontend): updated variable table

* feat(frontend): fix folder menu

* feat(frontend): fix folder menu

* fix(frontend): fix icons

* fix(frontend): reduce table size
This commit is contained in:
Faton Ramadani
2023-11-07 15:13:45 +01:00
committed by GitHub
parent 0951431e41
commit b040a89b27
5 changed files with 147 additions and 91 deletions

View File

@@ -0,0 +1,51 @@
<script lang="ts">
import { MoreVertical } from 'lucide-svelte'
import Menu from './common/menu/MenuV2.svelte'
import { MenuItem } from '@rgossiaux/svelte-headlessui'
import { twMerge } from 'tailwind-merge'
type Item = {
displayName: string
action?: (e: CustomEvent<any>) => void
icon: any
href?: string
disabled?: boolean
type?: 'action' | 'delete'
}
export let items: Item[] | (() => Item[]) = []
function computeItems(): Item[] {
if (typeof items === 'function') {
return items()
} else {
return items
}
}
</script>
<Menu placement="bottom-end" justifyEnd>
<div slot="trigger">
<MoreVertical size={16} class="w-8 h-8 p-2 hover:bg-surface-hover cursor-pointer rounded-md" />
</div>
<div class="flex flex-col">
{#each computeItems() ?? [] as item}
<MenuItem
on:click={(e) => item?.action?.(e)}
href={item?.href}
disabled={item?.disabled}
class={twMerge(
'px-4 py-2 text-primary hover:bg-surface-hover hover:text-primary cursor-pointer text-xs transition-all',
'flex flex-row gap-2 items-center',
item?.type === 'delete' && 'text-red-500 hover:bg-red-100 hover:text-red-500'
)}
>
{#if item.icon}
<svelte:component this={item.icon} size={14} />
{/if}
{item.displayName}
</MenuItem>
{/each}
</div>
</Menu>

View File

@@ -3,8 +3,10 @@
import Portal from 'svelte-portal'
import { offset, flip, shift } from 'svelte-floating-ui/dom'
import { createFloatingActions } from 'svelte-floating-ui'
import { twMerge } from 'tailwind-merge'
export let placement: any = 'bottom-start'
export let justifyEnd: boolean = false
const [floatingRef, floatingContent] = createFloatingActions({
strategy: 'fixed',
@@ -15,7 +17,7 @@
<Menu let:open as="div" class="relative hover:z-50 flex w-full h-8">
<div use:floatingRef class="w-full">
<MenuButton class="w-full">
<MenuButton class={twMerge('w-full', justifyEnd ? 'flex justify-end' : '')}>
<slot name="trigger" />
</MenuButton>
</div>

View File

@@ -34,10 +34,10 @@
numeric ? 'text-right' : '',
head ? 'font-semibold ' : '',
$$restProps.class,
sticky ? `!p-0 sticky ${first ? 'left-0' : 'right-0'}` : 'px-2 py-3.5',
size === 'sm' ? 'px-1.5 py-2.5' : '',
size === 'lg' ? 'px-3 py-4' : '',
size === 'xs' ? 'px-1 py-1.5' : '',
sticky ? `!p-0 sticky ${first ? 'left-0' : 'right-0'}` : 'px-2 py-3.5'
size === 'xs' ? 'px-1 py-1.5' : ''
)}
>
{#if sticky}

View File

@@ -3,11 +3,11 @@
import { FolderService } from '$lib/gen'
import CenteredPage from '$lib/components/CenteredPage.svelte'
import Dropdown from '$lib/components/Dropdown.svelte'
import Dropdown from '$lib/components/DropdownV2.svelte'
import FolderEditor from '$lib/components/FolderEditor.svelte'
import PageHeader from '$lib/components/PageHeader.svelte'
import { userStore, workspaceStore } from '$lib/stores'
import { faEdit, faPlus, faTrash } from '@fortawesome/free-solid-svg-icons'
import { faPlus } from '@fortawesome/free-solid-svg-icons'
import { Button, Drawer, DrawerContent, Popup, Skeleton } from '$lib/components/common'
import FolderInfo from '$lib/components/FolderInfo.svelte'
import FolderUsageInfo from '$lib/components/FolderUsageInfo.svelte'
@@ -16,6 +16,7 @@
import Head from '$lib/components/table/Head.svelte'
import Cell from '$lib/components/table/Cell.svelte'
import Row from '$lib/components/table/Row.svelte'
import { Pen, Trash } from 'lucide-svelte'
type FolderW = Folder & { canWrite: boolean }
@@ -159,11 +160,10 @@
<Cell><FolderInfo members={computeMembers(owners, extra_perms)} /></Cell>
<Cell shouldStopPropagation>
<Dropdown
placement="bottom-end"
dropdownItems={[
items={[
{
displayName: 'Manage folder',
icon: faEdit,
icon: Pen,
disabled: !canWrite,
action: () => {
editFolderName = name
@@ -172,8 +172,7 @@
},
{
displayName: 'Delete',
icon: faTrash,
icon: Trash,
type: 'delete',
disabled: !canWrite,
action: async () => {

View File

@@ -3,14 +3,17 @@
import { Alert, Badge, Button, Skeleton, Tab, Tabs } from '$lib/components/common'
import ConfirmationModal from '$lib/components/common/confirmationModal/ConfirmationModal.svelte'
import DeployWorkspaceDrawer from '$lib/components/DeployWorkspaceDrawer.svelte'
import Dropdown from '$lib/components/Dropdown.svelte'
import Dropdown from '$lib/components/DropdownV2.svelte'
import ListFilters from '$lib/components/home/ListFilters.svelte'
import PageHeader from '$lib/components/PageHeader.svelte'
import Popover from '$lib/components/Popover.svelte'
import SearchItems from '$lib/components/SearchItems.svelte'
import SharedBadge from '$lib/components/SharedBadge.svelte'
import ShareModal from '$lib/components/ShareModal.svelte'
import TableCustom from '$lib/components/TableCustom.svelte'
import Cell from '$lib/components/table/Cell.svelte'
import DataTable from '$lib/components/table/DataTable.svelte'
import Head from '$lib/components/table/Head.svelte'
import Row from '$lib/components/table/Row.svelte'
import TableSimple from '$lib/components/TableSimple.svelte'
import Tooltip from '$lib/components/Tooltip.svelte'
import VariableEditor from '$lib/components/VariableEditor.svelte'
@@ -19,18 +22,8 @@
import { userStore, workspaceStore } from '$lib/stores'
import { sendUserToast } from '$lib/toast'
import { canWrite, isOwner, truncate } from '$lib/utils'
import {
faChain,
faCircle,
faEdit,
faEyeSlash,
faFileExport,
faPlus,
faRefresh,
faShare,
faTrash
} from '@fortawesome/free-solid-svg-icons'
import { Building, DollarSign } from 'lucide-svelte'
import { faCircle, faEyeSlash, faPlus } from '@fortawesome/free-solid-svg-icons'
import { Building, DollarSign, FileUp, Link, Pen, RefreshCw, Share, Trash } from 'lucide-svelte'
import Icon from 'svelte-awesome'
type ListableVariableW = ListableVariable & { canWrite: boolean }
@@ -140,12 +133,13 @@
<Tab size="md" value="contextual">
<div class="flex gap-2 items-center my-1">
<DollarSign size={18} />
Contextual <Tooltip
Contextual
<Tooltip
documentationLink="https://www.windmill.dev/docs/core_concepts/variables_and_secrets#contextual-variables"
>
Contextual variables are passed as environment variables when running a script and depends
on the execution context.</Tooltip
>
on the execution context.
</Tooltip>
</div>
</Tab>
</Tabs>
@@ -153,41 +147,55 @@
<div class="pt-2">
<input placeholder="Search Variable" bind:value={filter} class="input mt-1" />
</div>
<ListFilters bind:selectedFilter={ownerFilter} filters={owners} />
<div class="min-h-[56px]">
<ListFilters bind:selectedFilter={ownerFilter} filters={owners} />
</div>
<div class="relative overflow-x-auto pb-40 pr-4">
{#if !filteredItems}
<Skeleton layout={[0.5, [2], 1]} />
{#each new Array(3) as _}
<Skeleton layout={[[3.5], 0.5]} />
{/each}
{:else if filteredItems.length == 0}
<div class="flex flex-col items-center justify-center h-full">
<div class="text-md font-medium">No variables found</div>
<div class="text-sm text-secondary">
Try changing the filters or creating a new variable
</div>
</div>
{:else}
<TableCustom>
<tr slot="header-row">
<th class="!px-0" />
<th>Path</th>
<th>Value</th>
<th>Description</th>
<th />
<th />
</tr>
<tbody slot="body">
<DataTable size="xs">
<Head>
<tr>
<Cell head first class="!px-0" />
<Cell head>Path</Cell>
<Cell head>Value</Cell>
<Cell head>Description</Cell>
<Cell head />
<Cell head last />
</tr>
</Head>
<tbody class="divide-y">
{#each filteredItems as { path, value, is_secret, description, extra_perms, canWrite, account, is_refreshed, is_expired, refresh_error, is_linked, marked }}
<tr>
<td class="!px-0 text-center">
<Row>
<Cell class="!px-0 text-center w-12" first>
<SharedBadge {canWrite} extraPerms={extra_perms} />
</td>
<td>
</Cell>
<Cell>
<a
class="break-all"
id="edit-{path}"
on:click={() => variableEditor.editVariable(path)}
href="#{path}"
>
{#if marked}{@html marked}{:else}{path}{/if}
{#if marked}
{@html marked}
{:else}
{path}
{/if}
</a>
</td>
<td>
</Cell>
<Cell>
<span class="inline-flex flex-row">
<span class="text-sm break-words">
{truncate(value ?? '****', 20)}
@@ -204,43 +212,39 @@
</Popover>
{/if}
</span>
</td>
<td class="break-words"
><span class="text-xs text-tertiary">{truncate(description ?? '', 50)}</span></td
>
</Cell>
<Cell class="break-words">
<span class="text-xs text-tertiary">{truncate(description ?? '', 50)} </span>
</Cell>
<td class="text-center">
<div class="flex flex-row">
<div class="w-10">
{#if is_linked}
<Popover notClickable>
<Icon data={faChain} />
<div slot="text">
This variable is linked with a resource of the same path. They are
deleted and renamed together.
</div>
</Popover>
{/if}
</div>
<div class="w-10">
{#if account}
<Popover notClickable>
<Icon data={faRefresh} />
<div slot="text">
This OAuth token will be kept up-to-date in the background by Windmill
using its refresh token
</div>
</Popover>
{/if}
</div>
<Cell class="text-center">
<div class="flex flex-row items-center gap-4">
{#if is_linked}
<Popover notClickable>
<Link size={16} />
<div slot="text">
This variable is linked with a resource of the same path. They are deleted
and renamed together.
</div>
</Popover>
{/if}
{#if account}
<Popover notClickable>
<RefreshCw size={16} />
<div slot="text">
This OAuth token will be kept up-to-date in the background by Windmill
using its refresh token
</div>
</Popover>
{/if}
{#if is_refreshed}
<div class="w-10">
<div class="">
{#if refresh_error}
<Popover notClickable>
<span class="flex h-4 w-4">
<div class="relative inline-flex justify-center items-center w-4 h-4">
<Icon
class="text-red-600 animate-ping absolute inline-flex "
class="text-red-600 animate-ping absolute z-50 w-4 h-4"
data={faCircle}
scale={0.7}
label="Error during exchange of the refresh token"
@@ -251,7 +255,8 @@
scale={0.7}
label="Error during exchange of the refresh token"
/>
</span>
</div>
<div slot="text">
Latest exchange of the refresh token did not succeed. Error: {refresh_error}
</div>
@@ -286,25 +291,24 @@
</div>
{/if}
</div>
</td>
<td>
</Cell>
<Cell last shouldStopPropagation>
<Dropdown
placement="bottom-end"
dropdownItems={() => {
items={() => {
let owner = isOwner(path, $userStore, $workspaceStore)
return [
{
displayName: 'Edit',
icon: faEdit,
icon: Pen,
action: () => variableEditor.editVariable(path),
disabled: !canWrite
},
{
displayName: 'Delete',
icon: faTrash,
icon: Trash,
type: 'delete',
action: (event) => {
if (event?.shiftKey) {
if (event['shiftKey']) {
deleteVariable(path, account)
} else {
deleteConfirmedCallback = () => {
@@ -316,7 +320,7 @@
},
{
displayName: 'Deploy to prod/staging',
icon: faFileExport,
icon: FileUp,
action: () => {
deploymentDrawer.openDrawer(path, 'variable')
}
@@ -326,13 +330,13 @@
action: () => {
shareModal.openDrawer(path, 'variable')
},
icon: faShare
icon: Share
},
...(account != undefined
? [
{
displayName: 'Refresh token',
icon: faRefresh,
icon: RefreshCw,
action: async () => {
await OauthService.refreshToken({
workspace: $workspaceStore ?? '',
@@ -350,11 +354,11 @@
]
}}
/>
</td>
</tr>
</Cell>
</Row>
{/each}
</tbody>
</TableCustom>
</DataTable>
{/if}
</div>
{:else if tab == 'contextual'}