@dxos/plugin-automation 0.8.4-main.2e9d522 → 0.8.4-main.3c1ae3b
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/dist/lib/browser/AutomationPanel-WVLTR65T.mjs +11 -0
- package/dist/lib/browser/AutomationSettings-LOCSJ4XL.mjs +68 -0
- package/dist/lib/browser/AutomationSettings-LOCSJ4XL.mjs.map +7 -0
- package/dist/lib/browser/FunctionsContainer-CQRS5IQN.mjs +145 -0
- package/dist/lib/browser/FunctionsContainer-CQRS5IQN.mjs.map +7 -0
- package/dist/lib/browser/{FunctionsPanel-56ZKRVM5.mjs → FunctionsPanel-CRW6SJUN.mjs} +3 -3
- package/dist/lib/browser/{app-graph-builder-ZTAUTFI4.mjs → app-graph-builder-W7LLC6XW.mjs} +13 -12
- package/dist/lib/browser/app-graph-builder-W7LLC6XW.mjs.map +7 -0
- package/dist/lib/browser/chunk-5Q2XVI36.mjs +100 -0
- package/dist/lib/browser/chunk-5Q2XVI36.mjs.map +7 -0
- package/dist/lib/browser/chunk-6FCNJOBO.mjs +15 -0
- package/dist/lib/browser/chunk-6FCNJOBO.mjs.map +7 -0
- package/dist/lib/browser/chunk-ABZVRMU7.mjs +14 -0
- package/dist/lib/browser/chunk-ABZVRMU7.mjs.map +7 -0
- package/dist/lib/browser/chunk-BPRH3NBB.mjs +204 -0
- package/dist/lib/browser/chunk-BPRH3NBB.mjs.map +7 -0
- package/dist/lib/browser/chunk-LZQFZO3B.mjs +17 -0
- package/dist/lib/browser/chunk-LZQFZO3B.mjs.map +7 -0
- package/dist/lib/browser/{chunk-LYJVTIVD.mjs → chunk-TWWFNOIR.mjs} +30 -15
- package/dist/lib/browser/chunk-TWWFNOIR.mjs.map +7 -0
- package/dist/lib/browser/chunk-WAZEZHJI.mjs +270 -0
- package/dist/lib/browser/chunk-WAZEZHJI.mjs.map +7 -0
- package/dist/lib/browser/{chunk-HN7OHFCB.mjs → chunk-YBPJCY3F.mjs} +3 -3
- package/dist/lib/browser/chunk-YBPJCY3F.mjs.map +7 -0
- package/dist/lib/browser/compute-runtime-GPT45IDG.mjs +115 -0
- package/dist/lib/browser/compute-runtime-GPT45IDG.mjs.map +7 -0
- package/dist/lib/browser/hooks/index.mjs +13 -0
- package/dist/lib/browser/index.mjs +49 -19
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/{intent-resolver-U3ZAQEFW.mjs → intent-resolver-MJ7IDWCD.mjs} +17 -14
- package/dist/lib/browser/intent-resolver-MJ7IDWCD.mjs.map +7 -0
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/{react-surface-4DFSM7OX.mjs → react-surface-FF5YP324.mjs} +20 -20
- package/dist/lib/browser/react-surface-FF5YP324.mjs.map +7 -0
- package/dist/lib/browser/types/index.mjs +2 -2
- package/dist/lib/node-esm/{AutomationPanel-YYUMSK2W.mjs → AutomationPanel-PKWGKR7X.mjs} +4 -4
- package/dist/lib/node-esm/AutomationSettings-V5GZEOOQ.mjs +69 -0
- package/dist/lib/node-esm/AutomationSettings-V5GZEOOQ.mjs.map +7 -0
- package/dist/lib/node-esm/FunctionsContainer-5BBSN6IG.mjs +146 -0
- package/dist/lib/node-esm/FunctionsContainer-5BBSN6IG.mjs.map +7 -0
- package/dist/lib/node-esm/{FunctionsPanel-KGIOZSPZ.mjs → FunctionsPanel-RVVCS6VH.mjs} +3 -3
- package/dist/lib/node-esm/FunctionsPanel-RVVCS6VH.mjs.map +7 -0
- package/dist/lib/node-esm/{app-graph-builder-3FP63ZSG.mjs → app-graph-builder-SLQOO7GH.mjs} +13 -12
- package/dist/lib/node-esm/app-graph-builder-SLQOO7GH.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-267XBACB.mjs +101 -0
- package/dist/lib/node-esm/chunk-267XBACB.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-6RH5OXPG.mjs +16 -0
- package/dist/lib/node-esm/chunk-6RH5OXPG.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-CEVIVRTY.mjs +19 -0
- package/dist/lib/node-esm/chunk-CEVIVRTY.mjs.map +7 -0
- package/dist/lib/node-esm/{chunk-OEZNHUL2.mjs → chunk-ECJKIUBO.mjs} +3 -3
- package/dist/lib/node-esm/chunk-ECJKIUBO.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-FOK5AUNF.mjs +16 -0
- package/dist/lib/node-esm/chunk-FOK5AUNF.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-JVPWGVIV.mjs +205 -0
- package/dist/lib/node-esm/chunk-JVPWGVIV.mjs.map +7 -0
- package/dist/lib/node-esm/{chunk-AZH66CED.mjs → chunk-Q7KY3LVQ.mjs} +104 -64
- package/dist/lib/node-esm/chunk-Q7KY3LVQ.mjs.map +7 -0
- package/dist/lib/node-esm/{chunk-ZGPUV5VS.mjs → chunk-RVK52XGK.mjs} +30 -15
- package/dist/lib/node-esm/chunk-RVK52XGK.mjs.map +7 -0
- package/dist/lib/node-esm/compute-runtime-SODV3TDU.mjs +116 -0
- package/dist/lib/node-esm/compute-runtime-SODV3TDU.mjs.map +7 -0
- package/dist/lib/node-esm/hooks/index.mjs +14 -0
- package/dist/lib/node-esm/hooks/index.mjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +49 -19
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/{intent-resolver-3QWXEBPX.mjs → intent-resolver-NOBCKCF6.mjs} +17 -14
- package/dist/lib/node-esm/intent-resolver-NOBCKCF6.mjs.map +7 -0
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/lib/node-esm/{react-surface-3PNW7NDW.mjs → react-surface-OQIMKKTP.mjs} +20 -20
- package/dist/lib/node-esm/react-surface-OQIMKKTP.mjs.map +7 -0
- package/dist/lib/node-esm/types/index.mjs +2 -2
- package/dist/types/src/AutomationPlugin.d.ts +1 -1
- package/dist/types/src/AutomationPlugin.d.ts.map +1 -1
- package/dist/types/src/capabilities/app-graph-builder.d.ts +1 -1
- package/dist/types/src/capabilities/app-graph-builder.d.ts.map +1 -1
- package/dist/types/src/capabilities/capabilities.d.ts +21 -0
- package/dist/types/src/capabilities/capabilities.d.ts.map +1 -0
- package/dist/types/src/capabilities/compute-runtime.d.ts +5 -0
- package/dist/types/src/capabilities/compute-runtime.d.ts.map +1 -0
- package/dist/types/src/capabilities/index.d.ts +5 -3
- package/dist/types/src/capabilities/index.d.ts.map +1 -1
- package/dist/types/src/capabilities/intent-resolver.d.ts +1 -1
- package/dist/types/src/capabilities/intent-resolver.d.ts.map +1 -1
- package/dist/types/src/capabilities/react-surface.d.ts +1 -1
- package/dist/types/src/capabilities/react-surface.d.ts.map +1 -1
- package/dist/types/src/components/AutomationPanel/AutomationPanel.d.ts +6 -5
- package/dist/types/src/components/AutomationPanel/AutomationPanel.d.ts.map +1 -1
- package/dist/types/src/components/AutomationPanel/AutomationPanel.stories.d.ts +49 -4
- package/dist/types/src/components/AutomationPanel/AutomationPanel.stories.d.ts.map +1 -1
- package/dist/types/src/components/AutomationSettings.d.ts +5 -0
- package/dist/types/src/components/AutomationSettings.d.ts.map +1 -0
- package/dist/types/src/components/FunctionsContainer.d.ts.map +1 -1
- package/dist/types/src/components/FunctionsPanel/FunctionsPanel.d.ts.map +1 -1
- package/dist/types/src/components/FunctionsRegistry/FunctionsRegistry.d.ts +8 -0
- package/dist/types/src/components/FunctionsRegistry/FunctionsRegistry.d.ts.map +1 -0
- package/dist/types/src/components/FunctionsRegistry/index.d.ts +2 -0
- package/dist/types/src/components/FunctionsRegistry/index.d.ts.map +1 -0
- package/dist/types/src/components/TriggerEditor/FunctionInputEditor.d.ts +10 -8
- package/dist/types/src/components/TriggerEditor/FunctionInputEditor.d.ts.map +1 -1
- package/dist/types/src/components/TriggerEditor/SpecSelector.d.ts +6 -3
- package/dist/types/src/components/TriggerEditor/SpecSelector.d.ts.map +1 -1
- package/dist/types/src/components/TriggerEditor/TriggerEditor.d.ts +9 -6
- package/dist/types/src/components/TriggerEditor/TriggerEditor.d.ts.map +1 -1
- package/dist/types/src/components/TriggerEditor/TriggerEditor.stories.d.ts +117 -4
- package/dist/types/src/components/TriggerEditor/TriggerEditor.stories.d.ts.map +1 -1
- package/dist/types/src/components/TriggerSettings.d.ts +6 -0
- package/dist/types/src/components/TriggerSettings.d.ts.map +1 -0
- package/dist/types/src/components/index.d.ts +2 -2
- package/dist/types/src/components/index.d.ts.map +1 -1
- package/dist/types/src/events.d.ts +4 -0
- package/dist/types/src/events.d.ts.map +1 -0
- package/dist/types/src/hooks/index.d.ts +3 -0
- package/dist/types/src/hooks/index.d.ts.map +1 -0
- package/dist/types/src/hooks/useComputeRuntimeCallback.d.ts +13 -0
- package/dist/types/src/hooks/useComputeRuntimeCallback.d.ts.map +1 -0
- package/dist/types/src/hooks/useTriggerRuntimeControls.d.ts +11 -0
- package/dist/types/src/hooks/useTriggerRuntimeControls.d.ts.map +1 -0
- package/dist/types/src/index.d.ts +3 -0
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/meta.d.ts +0 -1
- package/dist/types/src/meta.d.ts.map +1 -1
- package/dist/types/src/testing/test-functions.d.ts +2 -3
- package/dist/types/src/testing/test-functions.d.ts.map +1 -1
- package/dist/types/src/translations.d.ts +6 -0
- package/dist/types/src/translations.d.ts.map +1 -1
- package/dist/types/src/types/schema.d.ts +1 -1
- package/dist/types/src/types/schema.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +63 -38
- package/src/AutomationPlugin.tsx +37 -31
- package/src/capabilities/app-graph-builder.ts +16 -15
- package/src/capabilities/capabilities.ts +42 -0
- package/src/capabilities/compute-runtime.ts +129 -0
- package/src/capabilities/index.ts +3 -0
- package/src/capabilities/intent-resolver.ts +20 -15
- package/src/capabilities/react-surface.tsx +15 -15
- package/src/components/AutomationPanel/AutomationPanel.stories.tsx +16 -17
- package/src/components/AutomationPanel/AutomationPanel.tsx +127 -54
- package/src/components/AutomationSettings.tsx +30 -0
- package/src/components/FunctionsContainer.tsx +18 -13
- package/src/components/FunctionsPanel/FunctionsPanel.tsx +35 -16
- package/src/components/FunctionsRegistry/FunctionsRegistry.tsx +126 -0
- package/src/components/FunctionsRegistry/index.ts +5 -0
- package/src/components/TriggerEditor/FunctionInputEditor.tsx +40 -24
- package/src/components/TriggerEditor/SpecSelector.tsx +29 -21
- package/src/components/TriggerEditor/TriggerEditor.stories.tsx +78 -32
- package/src/components/TriggerEditor/TriggerEditor.tsx +86 -41
- package/src/components/TriggerSettings.tsx +25 -0
- package/src/components/index.ts +1 -1
- package/src/events.ts +11 -0
- package/src/hooks/index.ts +6 -0
- package/src/hooks/useComputeRuntimeCallback.ts +68 -0
- package/src/hooks/useTriggerRuntimeControls.ts +53 -0
- package/src/index.ts +3 -0
- package/src/meta.ts +6 -5
- package/src/testing/test-functions.ts +3 -3
- package/src/translations.ts +9 -0
- package/src/types/schema.ts +1 -1
- package/dist/lib/browser/AutomationContainer-VZNV2ZQF.mjs +0 -38
- package/dist/lib/browser/AutomationContainer-VZNV2ZQF.mjs.map +0 -7
- package/dist/lib/browser/AutomationPanel-ZWA6GOFY.mjs +0 -11
- package/dist/lib/browser/FunctionsContainer-IOB333TX.mjs +0 -39
- package/dist/lib/browser/FunctionsContainer-IOB333TX.mjs.map +0 -7
- package/dist/lib/browser/app-graph-builder-ZTAUTFI4.mjs.map +0 -7
- package/dist/lib/browser/chunk-ECSTS2UI.mjs +0 -14
- package/dist/lib/browser/chunk-ECSTS2UI.mjs.map +0 -7
- package/dist/lib/browser/chunk-ERTIGJYE.mjs +0 -147
- package/dist/lib/browser/chunk-ERTIGJYE.mjs.map +0 -7
- package/dist/lib/browser/chunk-FSJZXTS2.mjs +0 -230
- package/dist/lib/browser/chunk-FSJZXTS2.mjs.map +0 -7
- package/dist/lib/browser/chunk-GW5U2DGT.mjs +0 -15
- package/dist/lib/browser/chunk-GW5U2DGT.mjs.map +0 -7
- package/dist/lib/browser/chunk-HN7OHFCB.mjs.map +0 -7
- package/dist/lib/browser/chunk-LYJVTIVD.mjs.map +0 -7
- package/dist/lib/browser/intent-resolver-U3ZAQEFW.mjs.map +0 -7
- package/dist/lib/browser/react-surface-4DFSM7OX.mjs.map +0 -7
- package/dist/lib/node-esm/AutomationContainer-WMIH3F4V.mjs +0 -39
- package/dist/lib/node-esm/AutomationContainer-WMIH3F4V.mjs.map +0 -7
- package/dist/lib/node-esm/FunctionsContainer-DJWB6WFH.mjs +0 -40
- package/dist/lib/node-esm/FunctionsContainer-DJWB6WFH.mjs.map +0 -7
- package/dist/lib/node-esm/app-graph-builder-3FP63ZSG.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-AZH66CED.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-HIMYPGHF.mjs +0 -148
- package/dist/lib/node-esm/chunk-HIMYPGHF.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-NK5N3QKD.mjs +0 -17
- package/dist/lib/node-esm/chunk-NK5N3QKD.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-OEZNHUL2.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-SGZPTJ47.mjs +0 -16
- package/dist/lib/node-esm/chunk-SGZPTJ47.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-ZGPUV5VS.mjs.map +0 -7
- package/dist/lib/node-esm/intent-resolver-3QWXEBPX.mjs.map +0 -7
- package/dist/lib/node-esm/react-surface-3PNW7NDW.mjs.map +0 -7
- package/dist/types/src/components/AutomationContainer.d.ts +0 -5
- package/dist/types/src/components/AutomationContainer.d.ts.map +0 -1
- package/src/components/AutomationContainer.tsx +0 -30
- /package/dist/lib/browser/{AutomationPanel-ZWA6GOFY.mjs.map → AutomationPanel-WVLTR65T.mjs.map} +0 -0
- /package/dist/lib/browser/{FunctionsPanel-56ZKRVM5.mjs.map → FunctionsPanel-CRW6SJUN.mjs.map} +0 -0
- /package/dist/lib/{node-esm/AutomationPanel-YYUMSK2W.mjs.map → browser/hooks/index.mjs.map} +0 -0
- /package/dist/lib/node-esm/{FunctionsPanel-KGIOZSPZ.mjs.map → AutomationPanel-PKWGKR7X.mjs.map} +0 -0
|
@@ -2,56 +2,55 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import '@
|
|
6
|
-
|
|
7
|
-
import { type Meta } from '@storybook/react-vite';
|
|
5
|
+
import { type Meta, type StoryObj } from '@storybook/react-vite';
|
|
8
6
|
import React from 'react';
|
|
9
7
|
|
|
10
|
-
import {
|
|
11
|
-
import { FunctionType, FunctionTrigger } from '@dxos/functions';
|
|
8
|
+
import { Function, Trigger } from '@dxos/functions';
|
|
12
9
|
import { useSpaces } from '@dxos/react-client/echo';
|
|
13
10
|
import { withClientProvider } from '@dxos/react-client/testing';
|
|
14
|
-
import {
|
|
11
|
+
import { withTheme } from '@dxos/react-ui/testing';
|
|
15
12
|
|
|
16
|
-
import { AutomationPanel } from './AutomationPanel';
|
|
17
13
|
import { functions } from '../../testing';
|
|
18
14
|
import { translations } from '../../translations';
|
|
19
15
|
|
|
16
|
+
import { AutomationPanel } from './AutomationPanel';
|
|
17
|
+
|
|
20
18
|
const DefaultStory = () => {
|
|
21
19
|
const spaces = useSpaces();
|
|
22
20
|
const space = spaces[1];
|
|
23
21
|
|
|
24
22
|
return (
|
|
25
|
-
<div className='
|
|
23
|
+
<div className='is-96'>
|
|
26
24
|
<AutomationPanel space={space} />
|
|
27
25
|
</div>
|
|
28
26
|
);
|
|
29
27
|
};
|
|
30
28
|
|
|
31
|
-
const meta
|
|
29
|
+
const meta = {
|
|
32
30
|
title: 'plugins/plugin-automation/AutomationPanel',
|
|
33
|
-
component: AutomationPanel,
|
|
31
|
+
component: AutomationPanel as any,
|
|
34
32
|
render: DefaultStory,
|
|
35
33
|
decorators: [
|
|
34
|
+
withTheme,
|
|
36
35
|
withClientProvider({
|
|
37
36
|
createIdentity: true,
|
|
38
37
|
createSpace: true,
|
|
39
|
-
types: [
|
|
40
|
-
|
|
38
|
+
types: [Function.Function, Trigger.Trigger],
|
|
39
|
+
onCreateSpace: ({ space }) => {
|
|
41
40
|
for (const fn of functions) {
|
|
42
|
-
space.db.add(
|
|
41
|
+
space.db.add(Function.make(fn));
|
|
43
42
|
}
|
|
44
43
|
},
|
|
45
44
|
}),
|
|
46
|
-
withLayout(),
|
|
47
|
-
withTheme,
|
|
48
45
|
],
|
|
49
46
|
parameters: {
|
|
50
47
|
layout: 'centered',
|
|
51
48
|
translations,
|
|
52
49
|
},
|
|
53
|
-
}
|
|
50
|
+
} satisfies Meta<typeof DefaultStory>;
|
|
54
51
|
|
|
55
52
|
export default meta;
|
|
56
53
|
|
|
57
|
-
|
|
54
|
+
type Story = StoryObj<typeof meta>;
|
|
55
|
+
|
|
56
|
+
export const Default: Story = {};
|
|
@@ -2,60 +2,71 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import
|
|
6
|
-
import
|
|
7
|
-
|
|
8
|
-
import
|
|
9
|
-
import {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
TriggerKind,
|
|
16
|
-
} from '@dxos/functions';
|
|
5
|
+
import * as Array from 'effect/Array';
|
|
6
|
+
import * as EFn from 'effect/Function';
|
|
7
|
+
import * as Match from 'effect/Match';
|
|
8
|
+
import * as Schema from 'effect/Schema';
|
|
9
|
+
import React, { useMemo, useState } from 'react';
|
|
10
|
+
|
|
11
|
+
import { Filter, Obj, Tag } from '@dxos/echo';
|
|
12
|
+
import { Function, Script, Trigger } from '@dxos/functions';
|
|
13
|
+
import { FunctionsServiceClient } from '@dxos/functions-runtime/edge';
|
|
14
|
+
import { useTypeOptions } from '@dxos/plugin-space';
|
|
17
15
|
import { type Client, useClient } from '@dxos/react-client';
|
|
18
|
-
import {
|
|
19
|
-
import { Clipboard, IconButton, Input, Separator, useTranslation } from '@dxos/react-ui';
|
|
16
|
+
import { type Space, getSpace, useQuery } from '@dxos/react-client/echo';
|
|
17
|
+
import { Clipboard, IconButton, Input, Separator, type ThemedClassName, useTranslation } from '@dxos/react-ui';
|
|
20
18
|
import { ControlItem, controlItemClasses } from '@dxos/react-ui-form';
|
|
21
19
|
import { List } from '@dxos/react-ui-list';
|
|
22
20
|
import { ghostHover, mx } from '@dxos/react-ui-theme';
|
|
21
|
+
import { Project } from '@dxos/types';
|
|
22
|
+
import { isNonNullable } from '@dxos/util';
|
|
23
23
|
|
|
24
|
-
import {
|
|
24
|
+
import { meta } from '../../meta';
|
|
25
25
|
import { TriggerEditor, type TriggerEditorProps } from '../TriggerEditor';
|
|
26
26
|
|
|
27
|
-
const grid = 'grid grid-cols-[
|
|
27
|
+
const grid = 'grid grid-cols-[40px_1fr_32px_32px] min-bs-[2.5rem]';
|
|
28
28
|
|
|
29
|
-
export type AutomationPanelProps = {
|
|
29
|
+
export type AutomationPanelProps = ThemedClassName<{
|
|
30
30
|
space: Space;
|
|
31
31
|
object?: Obj.Any;
|
|
32
|
-
initialTrigger?:
|
|
32
|
+
initialTrigger?: Trigger.Trigger;
|
|
33
33
|
onDone?: () => void;
|
|
34
|
-
}
|
|
34
|
+
}>;
|
|
35
35
|
|
|
36
36
|
// TODO(burdon): Factor out common layout with ViewEditor.
|
|
37
|
-
export const AutomationPanel = ({ space, object, initialTrigger, onDone }: AutomationPanelProps) => {
|
|
38
|
-
const { t } = useTranslation(
|
|
37
|
+
export const AutomationPanel = ({ classNames, space, object, initialTrigger, onDone }: AutomationPanelProps) => {
|
|
38
|
+
const { t } = useTranslation(meta.id);
|
|
39
39
|
const client = useClient();
|
|
40
|
-
const
|
|
41
|
-
const functions = useQuery(space, Filter.type(
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
|
|
40
|
+
const functionsServiceClient = useMemo(() => FunctionsServiceClient.fromClient(client), [client]);
|
|
41
|
+
const functions = useQuery(space, Filter.type(Function.Function));
|
|
42
|
+
const triggers = useQuery(space, Filter.type(Trigger.Trigger));
|
|
43
|
+
const filteredTriggers = useMemo(() => {
|
|
44
|
+
return object ? triggers.filter(triggerMatch(object)) : triggers;
|
|
45
|
+
}, [object, triggers]);
|
|
46
|
+
const tags = useQuery(space, Filter.type(Tag.Tag));
|
|
47
|
+
const types = useTypeOptions({
|
|
48
|
+
space,
|
|
49
|
+
annotation: {
|
|
50
|
+
location: ['database', 'runtime'],
|
|
51
|
+
kind: ['user'],
|
|
52
|
+
registered: ['registered'],
|
|
53
|
+
},
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
const [trigger, setTrigger] = useState<Trigger.Trigger | undefined>(initialTrigger);
|
|
57
|
+
const [selected, setSelected] = useState<Trigger.Trigger>();
|
|
58
|
+
|
|
59
|
+
const handleSelect = (trigger: Trigger.Trigger) => {
|
|
60
|
+
setTrigger(trigger);
|
|
50
61
|
setSelected(trigger);
|
|
51
62
|
};
|
|
52
63
|
|
|
53
64
|
const handleAdd = () => {
|
|
54
|
-
setTrigger(
|
|
65
|
+
setTrigger(Trigger.make({}));
|
|
55
66
|
setSelected(undefined);
|
|
56
67
|
};
|
|
57
68
|
|
|
58
|
-
const handleDelete = (trigger:
|
|
69
|
+
const handleDelete = (trigger: Trigger.Trigger) => {
|
|
59
70
|
space.db.remove(trigger);
|
|
60
71
|
setTrigger(undefined);
|
|
61
72
|
setSelected(undefined);
|
|
@@ -65,7 +76,7 @@ export const AutomationPanel = ({ space, object, initialTrigger, onDone }: Autom
|
|
|
65
76
|
if (selected) {
|
|
66
77
|
Object.assign(selected, trigger);
|
|
67
78
|
} else {
|
|
68
|
-
space.db.add(
|
|
79
|
+
space.db.add(Trigger.make(trigger));
|
|
69
80
|
}
|
|
70
81
|
|
|
71
82
|
setTrigger(undefined);
|
|
@@ -78,27 +89,43 @@ export const AutomationPanel = ({ space, object, initialTrigger, onDone }: Autom
|
|
|
78
89
|
onDone?.();
|
|
79
90
|
};
|
|
80
91
|
|
|
92
|
+
const handleForceRunTrigger = async (trigger: Trigger.Trigger) => {
|
|
93
|
+
await functionsServiceClient.forceRunCronTrigger(space.id, trigger.id);
|
|
94
|
+
};
|
|
95
|
+
|
|
81
96
|
if (trigger) {
|
|
82
97
|
return (
|
|
83
98
|
<ControlItem title={t('trigger editor title')}>
|
|
84
|
-
<TriggerEditor
|
|
99
|
+
<TriggerEditor
|
|
100
|
+
space={space}
|
|
101
|
+
trigger={trigger}
|
|
102
|
+
readonlySpec={Boolean(object)}
|
|
103
|
+
tags={tags}
|
|
104
|
+
types={types}
|
|
105
|
+
onSave={handleSave}
|
|
106
|
+
onCancel={handleCancel}
|
|
107
|
+
/>
|
|
85
108
|
</ControlItem>
|
|
86
109
|
);
|
|
87
110
|
}
|
|
88
111
|
|
|
89
112
|
return (
|
|
90
|
-
<div className={controlItemClasses}>
|
|
91
|
-
{
|
|
92
|
-
<List.Root<
|
|
93
|
-
{
|
|
94
|
-
|
|
95
|
-
|
|
113
|
+
<div className={mx(controlItemClasses, classNames)}>
|
|
114
|
+
{filteredTriggers.length > 0 && (
|
|
115
|
+
<List.Root<Trigger.Trigger>
|
|
116
|
+
items={filteredTriggers}
|
|
117
|
+
isItem={Schema.is(Trigger.Trigger)}
|
|
118
|
+
getId={(field) => field.id}
|
|
119
|
+
>
|
|
120
|
+
{({ items: filteredTriggers }) => (
|
|
121
|
+
<div role='list' className='flex flex-col is-full'>
|
|
122
|
+
{filteredTriggers?.map((trigger) => {
|
|
96
123
|
const copyAction = getCopyAction(client, trigger);
|
|
97
124
|
return (
|
|
98
|
-
<List.Item<
|
|
125
|
+
<List.Item<Trigger.Trigger>
|
|
99
126
|
key={trigger.id}
|
|
100
127
|
item={trigger}
|
|
101
|
-
classNames={mx(grid, ghostHover, 'items-center', '
|
|
128
|
+
classNames={mx(grid, ghostHover, 'items-center', 'pli-2')}
|
|
102
129
|
>
|
|
103
130
|
<Input.Root>
|
|
104
131
|
<Input.Switch
|
|
@@ -109,10 +136,10 @@ export const AutomationPanel = ({ space, object, initialTrigger, onDone }: Autom
|
|
|
109
136
|
|
|
110
137
|
<div className={'flex'}>
|
|
111
138
|
<List.ItemTitle
|
|
112
|
-
classNames='
|
|
139
|
+
classNames='pli-1 cursor-pointer is-0 shrink truncate'
|
|
113
140
|
onClick={() => handleSelect(trigger)}
|
|
114
141
|
>
|
|
115
|
-
{getFunctionName(
|
|
142
|
+
{getFunctionName(functions, trigger) ?? '∅'}
|
|
116
143
|
</List.ItemTitle>
|
|
117
144
|
|
|
118
145
|
{/* TODO: a better way to expose copy action */}
|
|
@@ -124,6 +151,14 @@ export const AutomationPanel = ({ space, object, initialTrigger, onDone }: Autom
|
|
|
124
151
|
)}
|
|
125
152
|
</div>
|
|
126
153
|
|
|
154
|
+
<List.ItemButton
|
|
155
|
+
autoHide={false}
|
|
156
|
+
disabled={!trigger.enabled || trigger.spec?.kind !== 'timer'}
|
|
157
|
+
icon='ph--play--regular'
|
|
158
|
+
label='Force run'
|
|
159
|
+
onClick={() => handleForceRunTrigger(trigger)}
|
|
160
|
+
/>
|
|
161
|
+
|
|
127
162
|
<List.ItemDeleteButton onClick={() => handleDelete(trigger)} />
|
|
128
163
|
</List.Item>
|
|
129
164
|
);
|
|
@@ -132,25 +167,25 @@ export const AutomationPanel = ({ space, object, initialTrigger, onDone }: Autom
|
|
|
132
167
|
)}
|
|
133
168
|
</List.Root>
|
|
134
169
|
)}
|
|
135
|
-
{
|
|
170
|
+
{filteredTriggers.length > 0 && <Separator classNames='mlb-4' />}
|
|
136
171
|
<IconButton icon='ph--plus--regular' label={t('new trigger label')} onClick={handleAdd} />
|
|
137
172
|
</div>
|
|
138
173
|
);
|
|
139
174
|
};
|
|
140
175
|
|
|
141
|
-
const getCopyAction = (client: Client, trigger:
|
|
142
|
-
if (trigger?.spec?.kind ===
|
|
176
|
+
const getCopyAction = (client: Client, trigger: Trigger.Trigger | undefined) => {
|
|
177
|
+
if (trigger?.spec?.kind === 'email') {
|
|
143
178
|
return { translationKey: 'trigger copy email', contentProvider: () => `${getSpace(trigger)!.id}@dxos.network` };
|
|
144
179
|
}
|
|
145
180
|
|
|
146
|
-
if (trigger?.spec?.kind ===
|
|
181
|
+
if (trigger?.spec?.kind === 'webhook') {
|
|
147
182
|
return { translationKey: 'trigger copy url', contentProvider: () => getWebhookUrl(client, trigger) };
|
|
148
183
|
}
|
|
149
184
|
|
|
150
185
|
return undefined;
|
|
151
186
|
};
|
|
152
187
|
|
|
153
|
-
const getWebhookUrl = (client: Client, trigger:
|
|
188
|
+
const getWebhookUrl = (client: Client, trigger: Trigger.Trigger) => {
|
|
154
189
|
const spaceId = getSpace(trigger)!.id;
|
|
155
190
|
const edgeUrl = new URL(client.config.values.runtime!.services!.edge!.url!);
|
|
156
191
|
const isSecure = edgeUrl.protocol.startsWith('https') || edgeUrl.protocol.startsWith('wss');
|
|
@@ -158,14 +193,52 @@ const getWebhookUrl = (client: Client, trigger: FunctionTrigger) => {
|
|
|
158
193
|
return new URL(`/webhook/${spaceId}:${trigger.id}`, edgeUrl).toString();
|
|
159
194
|
};
|
|
160
195
|
|
|
161
|
-
const getFunctionName = (
|
|
196
|
+
const getFunctionName = (functions: Function.Function[], trigger: Trigger.Trigger) => {
|
|
162
197
|
// TODO(wittjosiah): Truncation should be done in the UI.
|
|
163
198
|
// Warning that the List component is currently a can of worms.
|
|
164
199
|
const shortId = trigger.function && `${trigger.function.dxn.toString().slice(0, 16)}…`;
|
|
165
200
|
const functionObject = functions.find((fn) => fn === trigger.function?.target);
|
|
166
|
-
|
|
167
|
-
|
|
201
|
+
return functionObject?.name ?? shortId;
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
const scriptMatch = (script: Script.Script) => (trigger: Trigger.Trigger) => {
|
|
205
|
+
const fn = trigger.function?.target;
|
|
206
|
+
if (!Obj.instanceOf(Function.Function, fn)) {
|
|
207
|
+
return false;
|
|
168
208
|
}
|
|
169
209
|
|
|
170
|
-
return
|
|
210
|
+
return fn.source?.target === script;
|
|
171
211
|
};
|
|
212
|
+
|
|
213
|
+
const projectMatch = (project: Project.Project) => {
|
|
214
|
+
const viewQueries = EFn.pipe(
|
|
215
|
+
project.columns,
|
|
216
|
+
Array.map((column) => column.view.target),
|
|
217
|
+
Array.filter(isNonNullable),
|
|
218
|
+
Array.map((view) => Obj.getSnapshot(view).query.ast),
|
|
219
|
+
Array.map((ast) => JSON.stringify(ast)),
|
|
220
|
+
);
|
|
221
|
+
|
|
222
|
+
return (trigger: Trigger.Trigger) => {
|
|
223
|
+
const spec = Obj.getSnapshot(trigger).spec;
|
|
224
|
+
if (spec?.kind !== 'subscription') {
|
|
225
|
+
return false;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// TODO(wittjosiah): Implement better way of comparing query ASTs.
|
|
229
|
+
return viewQueries.some((query) => JSON.stringify(spec.query) === query);
|
|
230
|
+
};
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
const triggerMatch = Match.type<Obj.Any>().pipe(
|
|
234
|
+
Match.withReturnType<(trigger: Trigger.Trigger) => boolean>(),
|
|
235
|
+
Match.when(
|
|
236
|
+
(obj) => Obj.instanceOf(Script.Script, obj),
|
|
237
|
+
(obj) => scriptMatch(obj),
|
|
238
|
+
),
|
|
239
|
+
Match.when(
|
|
240
|
+
(obj) => Obj.instanceOf(Project.Project, obj),
|
|
241
|
+
(obj) => projectMatch(obj),
|
|
242
|
+
),
|
|
243
|
+
Match.orElse((_obj) => () => true),
|
|
244
|
+
);
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import React from 'react';
|
|
6
|
+
|
|
7
|
+
import { useTranslation } from '@dxos/react-ui';
|
|
8
|
+
import { ControlPage, ControlSection } from '@dxos/react-ui-form';
|
|
9
|
+
|
|
10
|
+
import { meta } from '../meta';
|
|
11
|
+
|
|
12
|
+
import { AutomationPanel, type AutomationPanelProps } from './AutomationPanel';
|
|
13
|
+
import { TriggersSettings } from './TriggerSettings';
|
|
14
|
+
|
|
15
|
+
export const AutomationSettings = (props: AutomationPanelProps) => {
|
|
16
|
+
const { t } = useTranslation(meta.id);
|
|
17
|
+
return (
|
|
18
|
+
<ControlPage>
|
|
19
|
+
<ControlSection
|
|
20
|
+
title={t('automation verbose label', { ns: meta.id })}
|
|
21
|
+
description={t('automation description', { ns: meta.id })}
|
|
22
|
+
>
|
|
23
|
+
<AutomationPanel {...props} />
|
|
24
|
+
<TriggersSettings space={props.space} />
|
|
25
|
+
</ControlSection>
|
|
26
|
+
</ControlPage>
|
|
27
|
+
);
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export default AutomationSettings;
|
|
@@ -7,24 +7,29 @@ import React from 'react';
|
|
|
7
7
|
import { type Space } from '@dxos/react-client/echo';
|
|
8
8
|
import { useTranslation } from '@dxos/react-ui';
|
|
9
9
|
import { ControlPage, ControlSection } from '@dxos/react-ui-form';
|
|
10
|
-
|
|
10
|
+
|
|
11
|
+
import { meta } from '../meta';
|
|
11
12
|
|
|
12
13
|
import { FunctionsPanel } from './FunctionsPanel';
|
|
13
|
-
import {
|
|
14
|
+
import { FunctionsRegistry } from './FunctionsRegistry';
|
|
14
15
|
|
|
15
16
|
export const FunctionsContainer = ({ space }: { space: Space }) => {
|
|
16
|
-
const { t } = useTranslation(
|
|
17
|
+
const { t } = useTranslation(meta.id);
|
|
17
18
|
return (
|
|
18
|
-
<
|
|
19
|
-
<
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
19
|
+
<ControlPage>
|
|
20
|
+
<ControlSection
|
|
21
|
+
title={t('functions verbose label', { ns: meta.id })}
|
|
22
|
+
description={t('functions description', { ns: meta.id })}
|
|
23
|
+
>
|
|
24
|
+
<FunctionsPanel space={space} />
|
|
25
|
+
</ControlSection>
|
|
26
|
+
<ControlSection
|
|
27
|
+
title={t('functions registry verbose label', { ns: meta.id })}
|
|
28
|
+
description={t('functions registry description', { ns: meta.id })}
|
|
29
|
+
>
|
|
30
|
+
<FunctionsRegistry space={space} />
|
|
31
|
+
</ControlSection>
|
|
32
|
+
</ControlPage>
|
|
28
33
|
);
|
|
29
34
|
};
|
|
30
35
|
|
|
@@ -2,18 +2,21 @@
|
|
|
2
2
|
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import
|
|
5
|
+
import * as Schema from 'effect/Schema';
|
|
6
6
|
import React, { useCallback, useMemo } from 'react';
|
|
7
7
|
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
8
|
+
import { LayoutAction, createIntent } from '@dxos/app-framework';
|
|
9
|
+
import { useIntentDispatcher } from '@dxos/app-framework/react';
|
|
10
|
+
import { Obj } from '@dxos/echo';
|
|
11
|
+
import { Function, Script } from '@dxos/functions';
|
|
12
|
+
import { SpaceAction } from '@dxos/plugin-space/types';
|
|
13
|
+
import { Filter, type Space, useQuery } from '@dxos/react-client/echo';
|
|
14
|
+
import { Button, IconButton, useTranslation } from '@dxos/react-ui';
|
|
12
15
|
import { controlItemClasses } from '@dxos/react-ui-form';
|
|
13
16
|
import { List } from '@dxos/react-ui-list';
|
|
14
17
|
import { ghostHover, mx } from '@dxos/react-ui-theme';
|
|
15
18
|
|
|
16
|
-
import {
|
|
19
|
+
import { meta } from '../../meta';
|
|
17
20
|
|
|
18
21
|
const grid = 'grid grid-cols-[1fr_auto] min-bs-[2.5rem]';
|
|
19
22
|
|
|
@@ -22,9 +25,9 @@ export type FunctionsPanelProps = {
|
|
|
22
25
|
};
|
|
23
26
|
|
|
24
27
|
export const FunctionsPanel = ({ space }: FunctionsPanelProps) => {
|
|
25
|
-
const { t } = useTranslation(
|
|
26
|
-
const functions = useQuery(space, Filter.type(
|
|
27
|
-
const scripts = useQuery(space, Filter.type(
|
|
28
|
+
const { t } = useTranslation(meta.id);
|
|
29
|
+
const functions = useQuery(space, Filter.type(Function.Function));
|
|
30
|
+
const scripts = useQuery(space, Filter.type(Script.Script));
|
|
28
31
|
const { dispatchPromise: dispatch } = useIntentDispatcher();
|
|
29
32
|
|
|
30
33
|
const functionToScriptMap = useMemo(
|
|
@@ -40,13 +43,13 @@ export const FunctionsPanel = ({ space }: FunctionsPanelProps) => {
|
|
|
40
43
|
}
|
|
41
44
|
return map;
|
|
42
45
|
},
|
|
43
|
-
{} as Record<string,
|
|
46
|
+
{} as Record<string, Script.Script>,
|
|
44
47
|
),
|
|
45
48
|
[functions, scripts],
|
|
46
49
|
);
|
|
47
50
|
|
|
48
51
|
const getScriptName = useCallback(
|
|
49
|
-
(func:
|
|
52
|
+
(func: Function.Function) => {
|
|
50
53
|
const script = functionToScriptMap[func.id];
|
|
51
54
|
return script?.name;
|
|
52
55
|
},
|
|
@@ -54,23 +57,33 @@ export const FunctionsPanel = ({ space }: FunctionsPanelProps) => {
|
|
|
54
57
|
);
|
|
55
58
|
|
|
56
59
|
const handleGoToScript = useCallback(
|
|
57
|
-
(func:
|
|
60
|
+
(func: Function.Function) => {
|
|
58
61
|
const script = functionToScriptMap[func.id];
|
|
59
62
|
if (script) {
|
|
60
|
-
void dispatch(
|
|
63
|
+
void dispatch(
|
|
64
|
+
createIntent(LayoutAction.Open, {
|
|
65
|
+
part: 'main',
|
|
66
|
+
subject: [Obj.getDXN(script).toString()],
|
|
67
|
+
}),
|
|
68
|
+
);
|
|
61
69
|
}
|
|
62
70
|
},
|
|
63
71
|
[functionToScriptMap, dispatch],
|
|
64
72
|
);
|
|
65
73
|
|
|
74
|
+
const handleDelete = useCallback(
|
|
75
|
+
(func: Function.Function) => dispatch(createIntent(SpaceAction.RemoveObjects, { objects: [func] })),
|
|
76
|
+
[dispatch],
|
|
77
|
+
);
|
|
78
|
+
|
|
66
79
|
return (
|
|
67
80
|
<div role='none' className={mx(controlItemClasses)}>
|
|
68
81
|
{functions.length > 0 && (
|
|
69
|
-
<List.Root<
|
|
82
|
+
<List.Root<Function.Function> items={functions} isItem={Schema.is(Function.Function)} getId={(func) => func.id}>
|
|
70
83
|
{({ items }) => (
|
|
71
|
-
<div role='list' className='flex flex-col
|
|
84
|
+
<div role='list' className='flex flex-col is-full'>
|
|
72
85
|
{items?.map((func) => (
|
|
73
|
-
<List.Item<
|
|
86
|
+
<List.Item<Function.Function>
|
|
74
87
|
key={func.id}
|
|
75
88
|
item={func}
|
|
76
89
|
classNames={mx(grid, ghostHover, 'items-center', 'pli-2', 'min-bs-[3rem]')}
|
|
@@ -84,6 +97,12 @@ export const FunctionsPanel = ({ space }: FunctionsPanelProps) => {
|
|
|
84
97
|
{functionToScriptMap[func.id] && (
|
|
85
98
|
<Button onClick={() => handleGoToScript(func)}>{t('go to function source button label')}</Button>
|
|
86
99
|
)}
|
|
100
|
+
<IconButton
|
|
101
|
+
iconOnly
|
|
102
|
+
icon='ph--trash--regular'
|
|
103
|
+
label={t('delete function button label')}
|
|
104
|
+
onClick={() => handleDelete(func)}
|
|
105
|
+
/>
|
|
87
106
|
</List.Item>
|
|
88
107
|
))}
|
|
89
108
|
</div>
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import * as Array from 'effect/Array';
|
|
6
|
+
import * as EffectFunction from 'effect/Function';
|
|
7
|
+
import * as Order from 'effect/Order';
|
|
8
|
+
import * as Schema from 'effect/Schema';
|
|
9
|
+
import { useState } from 'react';
|
|
10
|
+
import React, { useCallback } from 'react';
|
|
11
|
+
|
|
12
|
+
import { Function } from '@dxos/functions';
|
|
13
|
+
import { getDeployedFunctions } from '@dxos/functions-runtime/edge';
|
|
14
|
+
import { useClient } from '@dxos/react-client';
|
|
15
|
+
import { Filter, Query, type Space, useQuery } from '@dxos/react-client/echo';
|
|
16
|
+
import { useAsyncEffect } from '@dxos/react-ui';
|
|
17
|
+
import { IconButton, useTranslation } from '@dxos/react-ui';
|
|
18
|
+
import { controlItemClasses } from '@dxos/react-ui-form';
|
|
19
|
+
import { List } from '@dxos/react-ui-list';
|
|
20
|
+
import { ghostHover, mx } from '@dxos/react-ui-theme';
|
|
21
|
+
|
|
22
|
+
import { meta } from '../../meta';
|
|
23
|
+
|
|
24
|
+
const grid = 'grid grid-cols-[1fr_1fr_auto] min-bs-[2.5rem]';
|
|
25
|
+
|
|
26
|
+
type FunctionsRegistryProps = {
|
|
27
|
+
space: Space;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export const FunctionsRegistry = ({ space }: FunctionsRegistryProps) => {
|
|
31
|
+
const client = useClient();
|
|
32
|
+
const [loading, setLoading] = useState(true);
|
|
33
|
+
const [functions, setFunctions] = useState<Function.Function[]>([]);
|
|
34
|
+
const { t } = useTranslation(meta.id);
|
|
35
|
+
|
|
36
|
+
const dbFunctions = useQuery(space, Filter.type(Function.Function));
|
|
37
|
+
|
|
38
|
+
const state = (func: Function.Function) => {
|
|
39
|
+
const dbFunction = dbFunctions.find((f) => f.key === func.key);
|
|
40
|
+
if (!dbFunction) {
|
|
41
|
+
return 'import';
|
|
42
|
+
}
|
|
43
|
+
if (dbFunction.version === func.version && dbFunction.updated === func.updated) {
|
|
44
|
+
return 'none';
|
|
45
|
+
}
|
|
46
|
+
return 'update';
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
useAsyncEffect(async () => {
|
|
50
|
+
setLoading(true);
|
|
51
|
+
const functions = await getDeployedFunctions(client);
|
|
52
|
+
setFunctions(functions);
|
|
53
|
+
setLoading(false);
|
|
54
|
+
}, []);
|
|
55
|
+
|
|
56
|
+
const dedupedFunctions = EffectFunction.pipe(
|
|
57
|
+
functions,
|
|
58
|
+
Array.filter((_) => _.key !== undefined),
|
|
59
|
+
Array.sort(Order.reverse(Order.mapInput(Order.string, (_: Function.Function) => _.updated ?? ''))),
|
|
60
|
+
Array.dedupeWith((self, that) => self.key === that.key),
|
|
61
|
+
Array.sort(Order.mapInput(Order.string, (_: Function.Function) => _.key ?? '')),
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
const hanleImportOrUpdate = useCallback(
|
|
65
|
+
async (func: Function.Function) => {
|
|
66
|
+
const functions = await space.db.query(Query.type(Function.Function, { key: func.key })).run();
|
|
67
|
+
const [existingFunc] = functions;
|
|
68
|
+
if (!existingFunc) {
|
|
69
|
+
space.db.add(func);
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
Function.setFrom(existingFunc, func);
|
|
73
|
+
},
|
|
74
|
+
[space],
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
return (
|
|
78
|
+
<div role='none' className={mx(controlItemClasses)}>
|
|
79
|
+
{dedupedFunctions.length > 0 && (
|
|
80
|
+
<List.Root<Function.Function>
|
|
81
|
+
items={dedupedFunctions}
|
|
82
|
+
isItem={Schema.is(Function.Function)}
|
|
83
|
+
getId={(func) => func.id}
|
|
84
|
+
>
|
|
85
|
+
{({ items }) => (
|
|
86
|
+
<div role='list' className='flex flex-col is-full'>
|
|
87
|
+
{items?.map((func) => (
|
|
88
|
+
<List.Item<Function.Function>
|
|
89
|
+
key={func.id}
|
|
90
|
+
item={func}
|
|
91
|
+
classNames={mx(grid, ghostHover, 'items-center', 'pli-2', 'min-bs-[3rem]')}
|
|
92
|
+
>
|
|
93
|
+
<div className='flex flex-col truncate'>
|
|
94
|
+
<List.ItemTitle classNames='truncate'>{func.name}</List.ItemTitle>
|
|
95
|
+
<div className='text-xs text-description truncate'>{func.key}</div>
|
|
96
|
+
</div>
|
|
97
|
+
<div className='flex flex-col truncate'>
|
|
98
|
+
<div className='text-xs text-description truncate'>{func.version}</div>
|
|
99
|
+
<div className='text-xs text-description truncate'>
|
|
100
|
+
{func.updated ? `Uploaded ${new Date(func.updated).toLocaleString()}` : ''}
|
|
101
|
+
</div>
|
|
102
|
+
</div>
|
|
103
|
+
|
|
104
|
+
<IconButton
|
|
105
|
+
iconOnly
|
|
106
|
+
icon={state(func) === 'update' ? 'ph--arrows-clockwise--regular' : 'ph--download--regular'}
|
|
107
|
+
label={
|
|
108
|
+
state(func) === 'update' ? t('update function button label') : t('import function button label')
|
|
109
|
+
}
|
|
110
|
+
disabled={state(func) === 'none'}
|
|
111
|
+
onClick={() => hanleImportOrUpdate(func)}
|
|
112
|
+
/>
|
|
113
|
+
</List.Item>
|
|
114
|
+
))}
|
|
115
|
+
</div>
|
|
116
|
+
)}
|
|
117
|
+
</List.Root>
|
|
118
|
+
)}
|
|
119
|
+
|
|
120
|
+
{dedupedFunctions.length === 0 && !loading && (
|
|
121
|
+
<div className='text-center plb-4 text-gray-500'>{t('no functions found')}</div>
|
|
122
|
+
)}
|
|
123
|
+
{loading && <div className='text-center plb-4 text-gray-500'>{t('loading functions')}</div>}
|
|
124
|
+
</div>
|
|
125
|
+
);
|
|
126
|
+
};
|