windmill-components 1.501.23 → 1.502.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package/components/Dev.svelte +4 -5
- package/package/components/apps/components/display/AppCarouselList.svelte +39 -4
- package/package/components/apps/editor/appUtils.js +4 -0
- package/package/components/apps/editor/componentsPanel/componentControlUtils.js +3 -1
- package/package/components/apps/editor/settingsPanel/ComponentPanelDataSource.svelte +0 -1
- package/package/components/auditLogs/AuditLogsFilters.svelte +12 -2
- package/package/components/auditLogs/AuditLogsTable.svelte +209 -122
- package/package/components/auditLogs/AuditLogsTable.svelte.d.ts +5 -20
- package/package/components/auditLogs/AuditLogsTimeline.svelte +449 -0
- package/package/components/auditLogs/AuditLogsTimeline.svelte.d.ts +16 -0
- package/package/components/copilot/chat/AIChatDisplay.svelte +23 -165
- package/package/components/copilot/chat/AIChatInput.svelte +128 -0
- package/package/components/copilot/chat/AIChatInput.svelte.d.ts +16 -0
- package/package/components/copilot/chat/AIChatManager.svelte.d.ts +4 -1
- package/package/components/copilot/chat/AIChatManager.svelte.js +33 -13
- package/package/components/copilot/chat/AIChatMessage.svelte +93 -0
- package/package/components/copilot/chat/AIChatMessage.svelte.d.ts +12 -0
- package/package/components/copilot/chat/ContextTextarea.svelte +13 -15
- package/package/components/copilot/chat/ContextTextarea.svelte.d.ts +4 -2
- package/package/components/copilot/chat/flow/FlowAIChat.svelte +11 -0
- package/package/components/copilot/chat/shared.d.ts +13 -3
- package/package/components/flow_builder.d.ts +10 -1
- package/package/components/graph/FlowGraphV2.svelte +1 -0
- package/package/components/schema/EditableSchemaWrapper.svelte +3 -3
- package/package/components/search/GlobalSearchModal.svelte +28 -18
- package/package/gen/core/OpenAPI.js +1 -1
- package/package/gen/schemas.gen.d.ts +11 -2
- package/package/gen/schemas.gen.js +11 -2
- package/package/gen/types.gen.d.ts +5 -2
- package/package.json +2 -1
|
@@ -441,22 +441,21 @@ let token = $derived($page.url.searchParams.get('wm_token') ?? undefined);
|
|
|
441
441
|
let workspace = $derived($page.url.searchParams.get('workspace') ?? undefined);
|
|
442
442
|
let themeDarkRaw = $derived($page.url.searchParams.get('activeColorTheme'));
|
|
443
443
|
let themeDark = $derived(themeDarkRaw == '2' || themeDarkRaw == '4');
|
|
444
|
-
$effect(() => {
|
|
444
|
+
$effect.pre(() => {
|
|
445
445
|
if (token) {
|
|
446
446
|
OpenAPI.WITH_CREDENTIALS = true;
|
|
447
447
|
OpenAPI.TOKEN = token;
|
|
448
|
-
untrack(() => loadUser());
|
|
449
448
|
}
|
|
450
449
|
});
|
|
451
|
-
$effect(() => {
|
|
450
|
+
$effect.pre(() => {
|
|
452
451
|
if (workspace) {
|
|
453
452
|
$workspaceStore = workspace;
|
|
454
|
-
untrack(() => setupCopilotInfo());
|
|
455
453
|
}
|
|
456
454
|
});
|
|
457
|
-
$effect(() => {
|
|
455
|
+
$effect.pre(() => {
|
|
458
456
|
if (workspace && token) {
|
|
459
457
|
untrack(() => loadUser());
|
|
458
|
+
untrack(() => setupCopilotInfo());
|
|
460
459
|
}
|
|
461
460
|
});
|
|
462
461
|
$effect(() => {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<script lang="ts">import { getContext } from 'svelte';
|
|
1
|
+
<script lang="ts">import { getContext, untrack } from 'svelte';
|
|
2
2
|
import { initConfig, initOutput } from '../../editor/appUtils';
|
|
3
3
|
import SubGridEditor from '../../editor/SubGridEditor.svelte';
|
|
4
4
|
import { initCss } from '../../utils';
|
|
@@ -13,15 +13,16 @@ import { Button } from '../../../common';
|
|
|
13
13
|
import { twMerge } from 'tailwind-merge';
|
|
14
14
|
import ResolveStyle from '../helpers/ResolveStyle.svelte';
|
|
15
15
|
let { id, componentInput, configuration, customCss = undefined, render, initializing = $bindable(), componentContainerHeight } = $props();
|
|
16
|
-
const { app, focusedGrid, selectedComponent, worldStore, connectingInput } = getContext('AppViewerContext');
|
|
16
|
+
const { app, focusedGrid, selectedComponent, worldStore, connectingInput, componentControl } = getContext('AppViewerContext');
|
|
17
17
|
let everRender = $state(render);
|
|
18
18
|
$effect.pre(() => {
|
|
19
19
|
render && !everRender && (everRender = true);
|
|
20
20
|
});
|
|
21
|
-
|
|
21
|
+
let outputs = initOutput($worldStore, id, {
|
|
22
22
|
result: undefined,
|
|
23
23
|
loading: false,
|
|
24
|
-
inputs: {}
|
|
24
|
+
inputs: {},
|
|
25
|
+
currentIndex: 0
|
|
25
26
|
});
|
|
26
27
|
const resolvedConfig = $state(initConfig(components['carousellistcomponent'].initialData.configuration, configuration));
|
|
27
28
|
function onFocus() {
|
|
@@ -43,6 +44,40 @@ $effect.pre(() => {
|
|
|
43
44
|
});
|
|
44
45
|
});
|
|
45
46
|
let currentPageIndex = $state(0);
|
|
47
|
+
// Single update function - ONLY place that calls .set()
|
|
48
|
+
function handleIndexChange() {
|
|
49
|
+
if (outputs?.currentIndex) {
|
|
50
|
+
if (!Array.isArray(result) || result.length === 0) {
|
|
51
|
+
// No data or empty data - reset to 0
|
|
52
|
+
currentPageIndex = 0;
|
|
53
|
+
outputs.currentIndex.set(0);
|
|
54
|
+
}
|
|
55
|
+
else if (currentPageIndex >= result.length) {
|
|
56
|
+
// Current index is out of bounds - reset to 0
|
|
57
|
+
currentPageIndex = 0;
|
|
58
|
+
outputs.currentIndex.set(0);
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
// Valid data and valid current index
|
|
62
|
+
outputs.currentIndex.set(currentPageIndex);
|
|
63
|
+
// Navigate carousel to match the current index
|
|
64
|
+
if (carousel) {
|
|
65
|
+
carousel.goTo(currentPageIndex);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
// Watch for changes and call update function (like working components)
|
|
71
|
+
$effect.pre(() => {
|
|
72
|
+
currentPageIndex != undefined && untrack(() => handleIndexChange());
|
|
73
|
+
});
|
|
74
|
+
$componentControl[id] = {
|
|
75
|
+
setSelectedIndex: (index) => {
|
|
76
|
+
if (Array.isArray(result) && index >= 0 && index < result.length) {
|
|
77
|
+
currentPageIndex = index;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
};
|
|
46
81
|
</script>
|
|
47
82
|
|
|
48
83
|
{#each Object.keys(components['carousellistcomponent'].initialData.configuration) as key (key)}
|
|
@@ -923,6 +923,7 @@ export function isContainer(type) {
|
|
|
923
923
|
type === 'horizontalsplitpanescomponent' ||
|
|
924
924
|
type === 'steppercomponent' ||
|
|
925
925
|
type === 'listcomponent' ||
|
|
926
|
+
type === 'carousellistcomponent' ||
|
|
926
927
|
type === 'decisiontreecomponent');
|
|
927
928
|
}
|
|
928
929
|
export function subGridIndexKey(type, id, world) {
|
|
@@ -938,6 +939,9 @@ export function subGridIndexKey(type, id, world) {
|
|
|
938
939
|
case 'steppercomponent': {
|
|
939
940
|
return world?.outputsById?.[id]?.currentStepIndex?.peak() ?? 0;
|
|
940
941
|
}
|
|
942
|
+
case 'carousellistcomponent': {
|
|
943
|
+
return world?.outputsById?.[id]?.currentIndex?.peak() ?? 0;
|
|
944
|
+
}
|
|
941
945
|
case 'decisiontreecomponent': {
|
|
942
946
|
return world?.outputsById?.[id]?.currentNodeIndex?.peak() ?? 0;
|
|
943
947
|
}
|
|
@@ -25,7 +25,7 @@ const setValue = {
|
|
|
25
25
|
};
|
|
26
26
|
const setSelectedIndex = {
|
|
27
27
|
title: 'setSelectedIndex',
|
|
28
|
-
description: 'Use the setSelectedIndex function to select a row in a table
|
|
28
|
+
description: 'Use the setSelectedIndex function to select a row in a table, an AG Grid table, or navigate to a slide in a Carousel component.',
|
|
29
29
|
example: 'setSelectedIndex(id: string, index: number)',
|
|
30
30
|
documentation: 'https://www.windmill.dev/docs/apps/app-runnable-panel#setselectedindex'
|
|
31
31
|
};
|
|
@@ -70,6 +70,8 @@ export function getComponentControl(type) {
|
|
|
70
70
|
return [setTab, setValue, setSelectedIndex];
|
|
71
71
|
case 'selecttabcomponent':
|
|
72
72
|
return [setTab, setValue, setSelectedIndex];
|
|
73
|
+
case 'carousellistcomponent':
|
|
74
|
+
return [setSelectedIndex];
|
|
73
75
|
case 'conditionalwrapper':
|
|
74
76
|
case 'steppercomponent':
|
|
75
77
|
return [setTab];
|
|
@@ -8,7 +8,6 @@ import ConfirmationModal from '../../../common/confirmationModal/ConfirmationMod
|
|
|
8
8
|
import { createChartjsComponentDataset, createPlotlyComponentDataset } from '../appUtils';
|
|
9
9
|
let { component = $bindable(), children } = $props();
|
|
10
10
|
let convertToUIEditorCallback = $state(undefined);
|
|
11
|
-
console.log('FOOBAR', component.type);
|
|
12
11
|
let selected = $state((component.type === 'plotlycomponentv2' || component.type === 'chartjscomponentv2') &&
|
|
13
12
|
component.datasets !== undefined
|
|
14
13
|
? 'ui-editor'
|
|
@@ -45,6 +45,7 @@ $effect.pre(() => {
|
|
|
45
45
|
}
|
|
46
46
|
});
|
|
47
47
|
async function loadLogs(username, page, perPage, before, after, operation, resource, actionKind, scope) {
|
|
48
|
+
console.log("loading logs");
|
|
48
49
|
loading = true;
|
|
49
50
|
if (username == 'all') {
|
|
50
51
|
username = undefined;
|
|
@@ -73,6 +74,7 @@ async function loadLogs(username, page, perPage, before, after, operation, resou
|
|
|
73
74
|
});
|
|
74
75
|
hasMore = logs.length > 0 && logs.length === perPage;
|
|
75
76
|
loading = false;
|
|
77
|
+
console.log("loadede logs");
|
|
76
78
|
}
|
|
77
79
|
async function loadUsers() {
|
|
78
80
|
usernames =
|
|
@@ -283,24 +285,32 @@ $effect(() => {
|
|
|
283
285
|
<span class="text-xs absolute -top-4">After</span>
|
|
284
286
|
<input type="text" value={after ?? 'After'} disabled />
|
|
285
287
|
<CalendarPicker
|
|
288
|
+
clearable
|
|
286
289
|
date={after}
|
|
287
290
|
placement="bottom-end"
|
|
288
291
|
label="After"
|
|
289
|
-
on:change={
|
|
292
|
+
on:change={({ detail }) => {
|
|
290
293
|
after = new Date(detail).toISOString()
|
|
291
294
|
}}
|
|
295
|
+
on:clear={() => {
|
|
296
|
+
after = undefined
|
|
297
|
+
}}
|
|
292
298
|
/>
|
|
293
299
|
</div>
|
|
294
300
|
<div class="flex gap-1 relative w-full">
|
|
295
301
|
<span class="text-xs absolute -top-4">Before</span>
|
|
296
302
|
<input type="text" value={before ?? 'Before'} disabled />
|
|
297
303
|
<CalendarPicker
|
|
304
|
+
clearable
|
|
298
305
|
bind:date={before}
|
|
299
306
|
label="Before"
|
|
300
307
|
placement="bottom-end"
|
|
301
|
-
on:change={
|
|
308
|
+
on:change={({ detail }) => {
|
|
302
309
|
before = new Date(detail).toISOString()
|
|
303
310
|
}}
|
|
311
|
+
on:clear={() => {
|
|
312
|
+
before = undefined
|
|
313
|
+
}}
|
|
304
314
|
/>
|
|
305
315
|
</div>
|
|
306
316
|
|
|
@@ -1,21 +1,11 @@
|
|
|
1
1
|
<script lang="ts">import Badge from '../common/badge/Badge.svelte';
|
|
2
|
-
import Cell from '../table/Cell.svelte';
|
|
3
|
-
import DataTable from '../table/DataTable.svelte';
|
|
4
|
-
import Head from '../table/Head.svelte';
|
|
5
|
-
import Row from '../table/Row.svelte';
|
|
6
2
|
import { displayDate } from '../../utils';
|
|
7
|
-
import {
|
|
3
|
+
import { onMount, tick } from 'svelte';
|
|
8
4
|
import Button from '../common/button/Button.svelte';
|
|
9
|
-
import { ListFilter } from 'lucide-svelte';
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
export let hasMore = true;
|
|
14
|
-
export let actionKind = undefined;
|
|
15
|
-
export let operation = undefined;
|
|
16
|
-
export let selectedId = undefined;
|
|
17
|
-
export let usernameFilter = undefined;
|
|
18
|
-
export let resourceFilter = undefined;
|
|
5
|
+
import { ListFilter, ChevronLeft, ChevronRight } from 'lucide-svelte';
|
|
6
|
+
import VirtualList from '@tutorlatin/svelte-tiny-virtual-list';
|
|
7
|
+
import { twMerge } from 'tailwind-merge';
|
|
8
|
+
let { logs = [], pageIndex = $bindable(1), perPage = $bindable(100), hasMore = $bindable(true), actionKind = $bindable(), operation = $bindable(), selectedId = undefined, usernameFilter = $bindable(), resourceFilter = $bindable(), onselect } = $props();
|
|
19
9
|
function groupLogsByDay(logs) {
|
|
20
10
|
const groupedLogs = {};
|
|
21
11
|
if (!logs)
|
|
@@ -34,8 +24,39 @@ function groupLogsByDay(logs) {
|
|
|
34
24
|
}
|
|
35
25
|
return groupedLogs;
|
|
36
26
|
}
|
|
37
|
-
|
|
38
|
-
|
|
27
|
+
function flattenLogs(groupedLogs) {
|
|
28
|
+
const flatLogs = [];
|
|
29
|
+
for (const [date, logsByDay] of Object.entries(groupedLogs)) {
|
|
30
|
+
flatLogs.push({ type: 'date', date });
|
|
31
|
+
for (const log of logsByDay) {
|
|
32
|
+
flatLogs.push({ type: 'log', log });
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return flatLogs;
|
|
36
|
+
}
|
|
37
|
+
let tableHeight = $state(0);
|
|
38
|
+
let headerHeight = $state(0);
|
|
39
|
+
let footerHeight = $state(48);
|
|
40
|
+
function computeHeight() {
|
|
41
|
+
tableHeight =
|
|
42
|
+
document.querySelector('#audit-logs-table-wrapper').parentElement?.clientHeight ?? 0;
|
|
43
|
+
}
|
|
44
|
+
onMount(() => {
|
|
45
|
+
tick().then(computeHeight);
|
|
46
|
+
});
|
|
47
|
+
let groupedLogs = $derived(groupLogsByDay(logs));
|
|
48
|
+
let flatLogs = $derived(groupedLogs ? flattenLogs(groupedLogs) : undefined);
|
|
49
|
+
let stickyIndices = $derived.by(() => {
|
|
50
|
+
const nstickyIndices = [];
|
|
51
|
+
let index = 0;
|
|
52
|
+
for (const entry of flatLogs ?? []) {
|
|
53
|
+
if (entry.type === 'date') {
|
|
54
|
+
nstickyIndices.push(index);
|
|
55
|
+
}
|
|
56
|
+
index++;
|
|
57
|
+
}
|
|
58
|
+
return nstickyIndices;
|
|
59
|
+
});
|
|
39
60
|
function kindToBadgeColor(kind) {
|
|
40
61
|
if (kind == 'Execute') {
|
|
41
62
|
return 'blue';
|
|
@@ -53,118 +74,178 @@ function kindToBadgeColor(kind) {
|
|
|
53
74
|
}
|
|
54
75
|
</script>
|
|
55
76
|
|
|
56
|
-
<
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
pageIndex = (pageIndex ?? 1) - 1
|
|
62
|
-
}}
|
|
63
|
-
currentPage={pageIndex}
|
|
64
|
-
paginated
|
|
65
|
-
rounded={false}
|
|
66
|
-
size="sm"
|
|
67
|
-
{hasMore}
|
|
68
|
-
bind:perPage
|
|
77
|
+
<svelte:window onresize={() => computeHeight()} />
|
|
78
|
+
|
|
79
|
+
<div
|
|
80
|
+
class="divide-y min-w-[640px] h-full"
|
|
81
|
+
id="audit-logs-table-wrapper"
|
|
69
82
|
>
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
<
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
<Cell>
|
|
105
|
-
<div class="flex flex-row gap-2 items-center">
|
|
106
|
-
<div class="whitespace-nowrap overflow-x-auto no-scrollbar max-w-52">
|
|
107
|
-
{username}
|
|
108
|
-
{#if parameters && 'end_user' in parameters}
|
|
109
|
-
<span> ({parameters.end_user})</span>
|
|
110
|
-
{/if}
|
|
83
|
+
|
|
84
|
+
<div bind:clientHeight={headerHeight}>
|
|
85
|
+
<div
|
|
86
|
+
class="flex flex-row bg-surface-secondary sticky top-0 w-full p-2 pr-4 text-xs font-semibold"
|
|
87
|
+
>
|
|
88
|
+
<div class="w-1/12">ID</div>
|
|
89
|
+
<div class="w-3/12">Timestamp</div>
|
|
90
|
+
<div class="w-3/12">Username</div>
|
|
91
|
+
<div class="w-3/12">Operation</div>
|
|
92
|
+
<div class="w-2/12">Resource</div>
|
|
93
|
+
</div>
|
|
94
|
+
</div>
|
|
95
|
+
{#if logs?.length == 0}
|
|
96
|
+
<div class="text-xs text-secondary p-8"> No logs found for the selected filters. </div>
|
|
97
|
+
{:else}
|
|
98
|
+
<VirtualList
|
|
99
|
+
width="100%"
|
|
100
|
+
height={tableHeight - headerHeight - footerHeight}
|
|
101
|
+
itemCount={flatLogs?.length ?? 0}
|
|
102
|
+
itemSize={42}
|
|
103
|
+
overscanCount={20}
|
|
104
|
+
{stickyIndices}
|
|
105
|
+
scrollToAlignment="center"
|
|
106
|
+
>
|
|
107
|
+
{#snippet header()}{/snippet}
|
|
108
|
+
{#snippet children({ index, style })}
|
|
109
|
+
<div {style} class="w-full">
|
|
110
|
+
{#if flatLogs}
|
|
111
|
+
{@const logOrDate = flatLogs[index]}
|
|
112
|
+
|
|
113
|
+
{#if logOrDate}
|
|
114
|
+
{#if logOrDate?.type === 'date'}
|
|
115
|
+
<div class="bg-surface-secondary py-2 border-b font-semibold text-xs pl-5">
|
|
116
|
+
{logOrDate.date}
|
|
111
117
|
</div>
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
118
|
+
{:else}
|
|
119
|
+
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
|
120
|
+
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
121
|
+
<div
|
|
122
|
+
class={twMerge(
|
|
123
|
+
'flex flex-row items-center h-full w-full px-2 py-1 hover:bg-surface-hover cursor-pointer',
|
|
124
|
+
logOrDate.log.id === selectedId ? 'bg-blue-50 dark:bg-blue-900/50' : ''
|
|
125
|
+
)}
|
|
126
|
+
role="button"
|
|
127
|
+
tabindex="0"
|
|
128
|
+
onclick={() => {
|
|
129
|
+
onselect?.(logOrDate.log.id)
|
|
119
130
|
}}
|
|
120
|
-
/>
|
|
121
|
-
</div>
|
|
122
|
-
</Cell>
|
|
123
|
-
<Cell>
|
|
124
|
-
<div class="flex flex-row gap-1">
|
|
125
|
-
<Badge
|
|
126
|
-
on:click={() => {
|
|
127
|
-
actionKind = action_kind.toLocaleLowerCase()
|
|
128
|
-
}}
|
|
129
|
-
color={kindToBadgeColor(action_kind)}>{action_kind}</Badge
|
|
130
131
|
>
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
132
|
+
<div class="w-1/12 text-xs truncate">
|
|
133
|
+
{logOrDate.log.id}
|
|
134
|
+
</div>
|
|
135
|
+
<div class="w-3/12 text-xs">
|
|
136
|
+
{displayDate(logOrDate.log.timestamp)}
|
|
137
|
+
</div>
|
|
138
|
+
<div class="w-3/12 text-xs">
|
|
139
|
+
<div class="flex flex-row gap-2 items-center">
|
|
140
|
+
<div class="whitespace-nowrap overflow-x-auto no-scrollbar max-w-60">
|
|
141
|
+
{logOrDate.log.username}
|
|
142
|
+
{#if logOrDate.log.parameters && 'end_user' in logOrDate.log.parameters}
|
|
143
|
+
<span> ({logOrDate.log.parameters.end_user})</span>
|
|
144
|
+
{/if}
|
|
145
|
+
</div>
|
|
146
|
+
<Button
|
|
147
|
+
color="light"
|
|
148
|
+
size="xs2"
|
|
149
|
+
iconOnly
|
|
150
|
+
startIcon={{ icon: ListFilter }}
|
|
151
|
+
on:click={() => {
|
|
152
|
+
usernameFilter = logOrDate.log.username
|
|
153
|
+
}}
|
|
154
|
+
/>
|
|
155
|
+
</div>
|
|
156
|
+
</div>
|
|
157
|
+
<div class="w-3/12 text-xs">
|
|
158
|
+
<div class="flex flex-row gap-1">
|
|
159
|
+
<Badge
|
|
160
|
+
on:click={() => {
|
|
161
|
+
actionKind = logOrDate.log.action_kind.toLocaleLowerCase()
|
|
162
|
+
}}
|
|
163
|
+
color={kindToBadgeColor(logOrDate.log.action_kind)}
|
|
164
|
+
>
|
|
165
|
+
{logOrDate.log.action_kind}
|
|
166
|
+
</Badge>
|
|
167
|
+
<Badge
|
|
168
|
+
on:click={() => {
|
|
169
|
+
operation = logOrDate.log.operation
|
|
170
|
+
}}
|
|
171
|
+
>
|
|
172
|
+
{logOrDate.log.operation}
|
|
173
|
+
</Badge>
|
|
174
|
+
</div>
|
|
175
|
+
</div>
|
|
176
|
+
<div class="w-2/12 text-xs">
|
|
177
|
+
<div class="flex flex-row gap-2 items-center">
|
|
178
|
+
<div class="whitespace-nowrap overflow-x-auto no-scrollbar max-w-60">
|
|
179
|
+
{logOrDate.log.resource}
|
|
180
|
+
</div>
|
|
181
|
+
<Button
|
|
182
|
+
color="light"
|
|
183
|
+
size="xs2"
|
|
184
|
+
iconOnly
|
|
185
|
+
startIcon={{ icon: ListFilter }}
|
|
186
|
+
on:click={() => {
|
|
187
|
+
resourceFilter = logOrDate.log.resource
|
|
188
|
+
}}
|
|
189
|
+
/>
|
|
190
|
+
</div>
|
|
191
|
+
</div>
|
|
144
192
|
</div>
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
startIcon={{ icon: ListFilter }}
|
|
150
|
-
on:click={() => {
|
|
151
|
-
resourceFilter = resource
|
|
152
|
-
}}
|
|
153
|
-
/>
|
|
193
|
+
{/if}
|
|
194
|
+
{:else}
|
|
195
|
+
<div class="flex flex-row items-center h-full w-full px-2">
|
|
196
|
+
<div class="text-xs text-secondary">Loading...</div>
|
|
154
197
|
</div>
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
</tr>
|
|
198
|
+
{/if}
|
|
199
|
+
{:else}
|
|
200
|
+
<div class="flex flex-row items-center h-full w-full px-2">
|
|
201
|
+
<div class="text-xs text-secondary">Loading...</div>
|
|
202
|
+
</div>
|
|
203
|
+
{/if}
|
|
204
|
+
</div>
|
|
205
|
+
{/snippet}
|
|
206
|
+
{#snippet footer()}{/snippet}
|
|
207
|
+
</VirtualList>
|
|
166
208
|
{/if}
|
|
167
|
-
|
|
209
|
+
<!-- Pagination footer - always visible -->
|
|
210
|
+
<div class="flex flex-row justify-between items-center p-2 bg-surface-primary border-t">
|
|
211
|
+
<div class="flex flex-row gap-2 items-center">
|
|
212
|
+
<Button
|
|
213
|
+
color="light"
|
|
214
|
+
size="xs2"
|
|
215
|
+
startIcon={{ icon: ChevronLeft }}
|
|
216
|
+
on:click={() => {
|
|
217
|
+
pageIndex = (pageIndex ?? 1) - 1
|
|
218
|
+
}}
|
|
219
|
+
disabled={pageIndex <= 1}
|
|
220
|
+
>
|
|
221
|
+
Previous
|
|
222
|
+
</Button>
|
|
223
|
+
<span class="text-xs text-secondary px-2">Page {pageIndex}</span>
|
|
224
|
+
<Button
|
|
225
|
+
color="light"
|
|
226
|
+
size="xs2"
|
|
227
|
+
endIcon={{ icon: ChevronRight }}
|
|
228
|
+
on:click={() => {
|
|
229
|
+
pageIndex = (pageIndex ?? 1) + 1
|
|
230
|
+
}}
|
|
231
|
+
disabled={!hasMore}
|
|
232
|
+
>
|
|
233
|
+
Next
|
|
234
|
+
</Button>
|
|
235
|
+
</div>
|
|
236
|
+
<div class="flex flex-row gap-2 items-center">
|
|
237
|
+
<span class="text-xs text-secondary">Per page:</span>
|
|
238
|
+
<select
|
|
239
|
+
bind:value={perPage}
|
|
240
|
+
class="text-xs bg-transparent border border-gray-300 dark:border-gray-600 rounded px-2 py-1"
|
|
241
|
+
>
|
|
242
|
+
<option value={25}>25</option>
|
|
243
|
+
<option value={100}>100</option>
|
|
244
|
+
<option value={1000}>1000</option>
|
|
245
|
+
</select>
|
|
246
|
+
</div>
|
|
247
|
+
</div>
|
|
248
|
+
</div>
|
|
168
249
|
|
|
169
250
|
<style>
|
|
170
251
|
/* Hide scrollbar for Chrome, Safari and Opera */
|
|
@@ -176,4 +257,10 @@ function kindToBadgeColor(kind) {
|
|
|
176
257
|
.no-scrollbar {
|
|
177
258
|
-ms-overflow-style: none; /* IE and Edge */
|
|
178
259
|
scrollbar-width: none; /* Firefox */
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/* VirtualList scrollbar styling */
|
|
263
|
+
:global(.virtual-list-wrapper:hover::-webkit-scrollbar) {
|
|
264
|
+
width: 8px !important;
|
|
265
|
+
height: 8px !important;
|
|
179
266
|
}</style>
|
|
@@ -1,18 +1,5 @@
|
|
|
1
1
|
import type { AuditLog } from '../../gen';
|
|
2
|
-
interface
|
|
3
|
-
new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
|
|
4
|
-
$$bindings?: Bindings;
|
|
5
|
-
} & Exports;
|
|
6
|
-
(internal: unknown, props: Props & {
|
|
7
|
-
$$events?: Events;
|
|
8
|
-
$$slots?: Slots;
|
|
9
|
-
}): Exports & {
|
|
10
|
-
$set?: any;
|
|
11
|
-
$on?: any;
|
|
12
|
-
};
|
|
13
|
-
z_$$bindings?: Bindings;
|
|
14
|
-
}
|
|
15
|
-
declare const AuditLogsTable: $$__sveltets_2_IsomorphicComponent<{
|
|
2
|
+
interface Props {
|
|
16
3
|
logs?: AuditLog[];
|
|
17
4
|
pageIndex?: number | undefined;
|
|
18
5
|
perPage?: number | undefined;
|
|
@@ -22,10 +9,8 @@ declare const AuditLogsTable: $$__sveltets_2_IsomorphicComponent<{
|
|
|
22
9
|
selectedId?: number | undefined;
|
|
23
10
|
usernameFilter?: string | undefined;
|
|
24
11
|
resourceFilter?: string | undefined;
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
}, {}, {}, string>;
|
|
30
|
-
type AuditLogsTable = InstanceType<typeof AuditLogsTable>;
|
|
12
|
+
onselect?: (id: number) => void;
|
|
13
|
+
}
|
|
14
|
+
declare const AuditLogsTable: import("svelte").Component<Props, {}, "operation" | "perPage" | "hasMore" | "pageIndex" | "actionKind" | "usernameFilter" | "resourceFilter">;
|
|
15
|
+
type AuditLogsTable = ReturnType<typeof AuditLogsTable>;
|
|
31
16
|
export default AuditLogsTable;
|