Various fix (#2032)

* fix(frontend): Before legacy code + add confirmation modal when removing a user + fix folder stats

* fix(frontend): Add Date type narrowing

* fix(frontend): Add missing fullscreen in ScriptEditorDrawer

* fix(frontend): Check if we have un-saved changes before navigating to the homepage

* fix(frontend): correctly use the keyed each

* fix(frontend): add DateTimeInput

* fix(frontend): add DateTimeInput

* fix(frontend): add DateTimeInput

* fix(frontend): code cleanup

* fix(frontend): Set default time to 00:00
This commit is contained in:
Faton Ramadani
2023-08-11 18:38:45 +02:00
committed by GitHub
parent 117db98486
commit 38421684ed
12 changed files with 141 additions and 83 deletions
+2 -1
View File
@@ -26,6 +26,7 @@
import { twMerge } from 'tailwind-merge'
import ArgEnum from './ArgEnum.svelte'
import ArrayTypeNarrowing from './ArrayTypeNarrowing.svelte'
import DateTimeInput from './DateTimeInput.svelte'
export let label: string = ''
export let value: any
@@ -379,7 +380,7 @@
<ArgEnum {defaultValue} {valid} {customValue} {disabled} bind:value {enum_} {autofocus} />
</div>
{:else if inputCat == 'date'}
<input {autofocus} class="inline-block" type="datetime-local" bind:value />
<DateTimeInput {autofocus} bind:value />
{:else if inputCat == 'sql' || inputCat == 'yaml'}
<div class="border my-1 mb-4 w-full border-gray-400">
<SimpleEditor
@@ -0,0 +1,22 @@
<script lang="ts">
export let value: string | undefined = undefined
export let autofocus: boolean = false
let dateFromValue = value ? new Date(value) : undefined
let date = dateFromValue ? dateFromValue.toISOString().split('T')[0] : undefined
let time = dateFromValue ? dateFromValue.toISOString().split('T')[1] : '00:00'
$: {
if (date && time) {
let newDate = new Date(`${date}T${time}`)
value = newDate.toISOString()
}
}
</script>
<div class="flex flex-row gap-1 items-center w-full">
<!-- svelte-ignore a11y-autofocus -->
<input type="date" bind:value={date} {autofocus} class="!w-3/4" />
<input type="time" bind:value={time} class="!w-1/4" />
</div>
@@ -17,6 +17,7 @@
import { X } from 'lucide-svelte'
import LightweightResourcePicker from './LightweightResourcePicker.svelte'
import LightweightObjectResourceInput from './LightweightObjectResourceInput.svelte'
import DateTimeInput from './DateTimeInput.svelte'
export let css: ComponentCustomCSS<'schemaformcomponent'> | undefined = undefined
export let label: string = ''
@@ -311,7 +312,7 @@
{/each}
</select>
{:else if inputCat == 'date'}
<input class="inline-block" type="datetime-local" bind:value />
<DateTimeInput bind:value />
{:else if inputCat == 'base64'}
<input
type="file"
@@ -19,9 +19,9 @@
'uuid',
'ipv4',
'yaml',
'sql'
'sql',
// 'time',
// 'date',
'date-time'
// 'duration',
// 'ipv6',
// 'jsonpointer'
+29 -59
View File
@@ -1,68 +1,38 @@
<script lang="ts">
import { createEventDispatcher } from 'svelte'
// A table suitable if you can pass data as a list of row objects
import Cell from './table/Cell.svelte'
import DataTable from './table/DataTable.svelte'
import Head from './table/Head.svelte'
export let headers: string[] | undefined
export let data: any[] | undefined // Object containing the data
export let keys: string[]
export let defaultText: string = 'No data to display'
export let paginated = false
export let twTextSize: string = 'text-sm md:text-base'
const dispatch = createEventDispatcher()
</script>
<div class="mt-2 flex flex-col {$$props.class}">
<div class="inline-block min-w-full align-middle">
<table class="min-w-full divide-y table-auto">
<thead>
<tr class={twTextSize}>
{#if headers}
{#each headers as header, i}
<th
class="py-3.5 text-left text-sm font-semibold text-primary capitalize {i == 0
? 'sm:pl-6 md:pl-0 pl-4 pr-3'
: 'px-3 '}">{header}</th
>
{/each}
{/if}
</tr>
</thead>
<tbody class="divide-y">
{#if data && keys && data.length > 0}
{#each data as row}
<tr>
{#each keys as key, i}
<td
class="py-2 text-sm text-secondary break-words {i == 0
? 'pl-4 pr-3 sm:pl-6 md:pl-0 font-semibold'
: 'px-3'} {twTextSize}"
>
{row[key] ?? ''}</td
>
{/each}
</tr>
<div class="mt-2">
<DataTable>
<Head>
<tr>
{#if headers}
{#each headers as header, i}
<Cell first={i == 0} last={i == headers.length - 1} head>{header}</Cell>
{/each}
{:else if data}
<tr>{defaultText}</tr>
{:else}
<tr>Loading...</tr>
{/if}
</tbody>
</table>
</div>
</tr>
</Head>
<tbody class="divide-y">
{#if data && keys && data.length > 0}
{#each data as row}
<tr>
{#each keys as key, i}
<Cell first={i == 0} last={i == keys.length - 1} class="w-1/3 whitespace-pre-wrap">
{row[key] ?? ''}
</Cell>
{/each}
</tr>
{/each}
{:else}
<tr>Loading...</tr>
{/if}
</tbody>
</DataTable>
</div>
{#if paginated}
<div class="flex flex-row-reverse text-tertiary">
<button
on:click={() => {
dispatch('next')
}}>Next</button
>
<button
class="mx-2"
on:click={() => {
dispatch('next')
}}>Previous</button
>
</div>
{/if}
@@ -63,7 +63,7 @@
{...btn.buttonProps}
startIcon={{ icon: btn.buttonProps.startIcon }}
on:click={btn.buttonProps.onClick}
btnClasses="hidden md:block"
btnClasses="hidden md:flex items-center gap-1"
>
{btn.label}
</Button>
@@ -72,7 +72,7 @@
startIcon={{ icon: btn.buttonProps.startIcon }}
on:click={btn.buttonProps.onClick}
iconOnly
btnClasses="block md:hidden"
btnClasses="flex md:hidden items-center gap-1"
>
{btn.label}
</Button>
@@ -80,6 +80,7 @@
title="Script Editor"
noPadding
forceOverflowVisible
fullScreen
on:close={() => {
scriptEditorDrawer.closeDrawer()
}}
@@ -19,6 +19,7 @@
import SearchItems from '../SearchItems.svelte'
import Cell from '../table/Cell.svelte'
import Row from '../table/Row.svelte'
import ConfirmationModal from '../common/confirmationModal/ConfirmationModal.svelte'
let users: User[] | undefined = undefined
let invites: WorkspaceInvite[] = []
@@ -59,6 +60,8 @@
}
}
let deleteConfirmedCallback: (() => void) | undefined = undefined
async function removeAllInvitesFromDomain() {
await Promise.all(
invites
@@ -221,13 +224,15 @@
btnClasses="text-red-500"
size="xs"
spacingSize="xs2"
on:click={async () => {
await UserService.deleteUser({
workspace: $workspaceStore ?? '',
username
})
sendUserToast('User removed')
listUsers()
on:click={() => {
deleteConfirmedCallback = async () => {
await UserService.deleteUser({
workspace: $workspaceStore ?? '',
username
})
sendUserToast('User removed')
listUsers()
}
}}
>
Remove
@@ -360,8 +365,10 @@
})
loadSettings()
listInvites()
}}>Unset auto-invite from {auto_invite_domain} domain</Button
}}
>
Unset auto-invite from {auto_invite_domain} domain
</Button>
</div>
</div>
{/if}
@@ -369,3 +376,22 @@
{#if !allowedAutoDomain}
<div class="text-red-400 text-xs mb-2">{domain} domain not allowed for auto-invite</div>
{/if}
<ConfirmationModal
open={Boolean(deleteConfirmedCallback)}
title="Remove user"
confirmationText="Remove"
on:canceled={() => {
deleteConfirmedCallback = undefined
}}
on:confirmed={() => {
if (deleteConfirmedCallback) {
deleteConfirmedCallback()
}
deleteConfirmedCallback = undefined
}}
>
<div class="flex flex-col w-full space-y-4">
<span>Are you sure you want to remove ?</span>
</div>
</ConfirmationModal>
@@ -6,8 +6,49 @@
import Menu from '../common/menu/Menu.svelte'
import { faCog, faPlus } from '@fortawesome/free-solid-svg-icons'
import { Icon } from 'svelte-awesome'
import { goto } from '$app/navigation'
import { dirtyStore } from '../common/confirmationModal/dirtyStore'
import { page } from '$app/stores'
export let isCollapsed: boolean = false
function waitForNextUpdate(store) {
return new Promise((resolve) => {
let firstEmission = true
store.subscribe((value) => {
if (firstEmission) {
firstEmission = false
return
}
resolve(value)
})
})
}
async function toggleSwitchWorkspace(id: string) {
if ($workspaceStore === id) {
return
}
const editPages = ['/scripts/edit/', '/flows/edit/', '/apps/edit/']
const isOnEditPage = editPages.some((editPage) => $page.route.id?.includes(editPage) ?? false)
// Check if we have unsaved changes
const wasDirty = $dirtyStore
// Try to go to the home page
// If we weren't dirty, we can directly switch workspaces
if (!wasDirty && !isOnEditPage) {
switchWorkspace(id)
} else {
await goto('/')
const shouldSwitch = await waitForNextUpdate(dirtyStore)
if (shouldSwitch) {
switchWorkspace(id)
}
}
}
</script>
<Menu placement="bottom-start" let:close>
@@ -36,11 +77,8 @@
{$workspaceStore === workspace.id
? 'cursor-default bg-surface-selected'
: 'cursor-pointer hover:bg-surface-hover'}"
on:click={() => {
if ($workspaceStore === workspace.id) {
return
}
switchWorkspace(workspace.id)
on:click={async () => {
await toggleSwitchWorkspace(workspace.id)
close()
}}
>
@@ -127,7 +127,7 @@
</tr>
{/if}
{#each folders as { name, extra_perms, owners, canWrite }}
{#each folders as { name, extra_perms, owners, canWrite } (name)}
<Row
hoverable
on:click={() => {
@@ -109,7 +109,7 @@
</tr>
{/each}
{:else}
{#each groups as { name, summary, extra_perms, canWrite }}
{#each groups as { name, summary, extra_perms, canWrite } (name)}
<tr>
<td class="!px-0 text-center">
<SharedBadge {canWrite} extraPerms={extra_perms} />
@@ -366,10 +366,9 @@
{/each}
{:else}
<TableSimple
headers={['name', 'example of value', 'description']}
headers={['Name', 'Example of value', 'Description']}
data={contextualVariables}
keys={['name', 'value', 'description']}
twTextSize="text-sm"
/>
{/if}
</div>