@dxos/plugin-pipeline 0.8.4-main.3eb6e50203 → 0.8.4-main.c85a9c8dae
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/PipelineContainer-2ZYSZATA.mjs +320 -0
- package/dist/lib/browser/PipelineContainer-2ZYSZATA.mjs.map +7 -0
- package/dist/lib/browser/{PipelineSettings-YSGSJHBT.mjs → PipelineObjectSettings-D36OYVHW.mjs} +66 -54
- package/dist/lib/browser/PipelineObjectSettings-D36OYVHW.mjs.map +7 -0
- package/dist/lib/browser/{chunk-7GVESVYR.mjs → chunk-N6FZHYWT.mjs} +2 -2
- package/dist/lib/browser/chunk-N6FZHYWT.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +4 -4
- package/dist/lib/browser/index.mjs.map +3 -3
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/{react-surface-JMMCTIL3.mjs → react-surface-ZGQVH4VX.mjs} +11 -8
- package/dist/lib/browser/react-surface-ZGQVH4VX.mjs.map +7 -0
- package/dist/lib/browser/types/index.mjs +1 -1
- package/dist/types/src/capabilities/app-graph-builder/app-graph-builder.d.ts +1 -1
- package/dist/types/src/capabilities/operation-resolver/operation-resolver.d.ts +1 -1
- package/dist/types/src/capabilities/react-surface/react-surface.d.ts +1 -1
- package/dist/types/src/capabilities/react-surface/react-surface.d.ts.map +1 -1
- package/dist/types/src/components/PipelineColumn.d.ts +3 -4
- package/dist/types/src/components/PipelineColumn.d.ts.map +1 -1
- package/dist/types/src/components/PipelineComponent.d.ts +20 -11
- package/dist/types/src/components/PipelineComponent.d.ts.map +1 -1
- package/dist/types/src/components/PipelineComponent.stories.d.ts.map +1 -1
- package/dist/types/src/components/index.d.ts +2 -4
- package/dist/types/src/components/index.d.ts.map +1 -1
- package/dist/types/src/{components → containers/PipelineContainer}/PipelineContainer.d.ts +2 -3
- package/dist/types/src/containers/PipelineContainer/PipelineContainer.d.ts.map +1 -0
- package/dist/types/src/containers/PipelineContainer/PipelineContainer.stories.d.ts.map +1 -0
- package/dist/types/src/containers/PipelineContainer/index.d.ts +3 -0
- package/dist/types/src/containers/PipelineContainer/index.d.ts.map +1 -0
- package/dist/types/src/{components/PipelineSettings.d.ts → containers/PipelineObjectSettings/PipelineObjectSettings.d.ts} +1 -2
- package/dist/types/src/containers/PipelineObjectSettings/PipelineObjectSettings.d.ts.map +1 -0
- package/dist/types/src/containers/PipelineObjectSettings/index.d.ts +3 -0
- package/dist/types/src/containers/PipelineObjectSettings/index.d.ts.map +1 -0
- package/dist/types/src/containers/index.d.ts +4 -0
- package/dist/types/src/containers/index.d.ts.map +1 -0
- package/dist/types/src/hooks/index.d.ts +2 -0
- package/dist/types/src/hooks/index.d.ts.map +1 -0
- package/dist/types/src/hooks/usePipelineBoardModel.d.ts +6 -0
- package/dist/types/src/hooks/usePipelineBoardModel.d.ts.map +1 -0
- package/dist/types/src/templates/research.d.ts.map +1 -1
- package/dist/types/src/types/PipelineAction.d.ts +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +42 -39
- package/src/capabilities/operation-resolver/operation-resolver.ts +2 -2
- package/src/capabilities/react-surface/react-surface.tsx +7 -5
- package/src/components/PipelineColumn.tsx +24 -36
- package/src/components/PipelineComponent.stories.tsx +27 -14
- package/src/components/PipelineComponent.tsx +55 -27
- package/src/components/index.ts +2 -7
- package/src/{components → containers/PipelineContainer}/PipelineContainer.stories.tsx +91 -88
- package/src/containers/PipelineContainer/PipelineContainer.tsx +87 -0
- package/src/containers/PipelineContainer/index.ts +7 -0
- package/src/{components/PipelineSettings.tsx → containers/PipelineObjectSettings/PipelineObjectSettings.tsx} +65 -53
- package/src/containers/PipelineObjectSettings/index.ts +7 -0
- package/src/containers/index.ts +8 -0
- package/src/hooks/index.ts +5 -0
- package/src/hooks/usePipelineBoardModel.ts +79 -0
- package/src/templates/research.ts +14 -16
- package/src/types/PipelineAction.ts +1 -1
- package/dist/lib/browser/PipelineContainer-I3TPA7ZN.mjs +0 -217
- package/dist/lib/browser/PipelineContainer-I3TPA7ZN.mjs.map +0 -7
- package/dist/lib/browser/PipelineSettings-YSGSJHBT.mjs.map +0 -7
- package/dist/lib/browser/chunk-7GVESVYR.mjs.map +0 -7
- package/dist/lib/browser/react-surface-JMMCTIL3.mjs.map +0 -7
- package/dist/types/src/components/PipelineContainer.d.ts.map +0 -1
- package/dist/types/src/components/PipelineContainer.stories.d.ts.map +0 -1
- package/dist/types/src/components/PipelineSettings.d.ts.map +0 -1
- package/src/components/PipelineContainer.tsx +0 -57
- /package/dist/types/src/{components → containers/PipelineContainer}/PipelineContainer.stories.d.ts +0 -0
|
@@ -4,28 +4,30 @@
|
|
|
4
4
|
|
|
5
5
|
import { type Meta, type StoryObj } from '@storybook/react-vite';
|
|
6
6
|
import * as Effect from 'effect/Effect';
|
|
7
|
+
import * as Layer from 'effect/Layer';
|
|
7
8
|
import React from 'react';
|
|
8
9
|
|
|
9
10
|
import { withPluginManager } from '@dxos/app-framework/testing';
|
|
10
11
|
import { Filter, Ref } from '@dxos/client/echo';
|
|
11
|
-
import { Obj, Query, Tag
|
|
12
|
+
import { Database, Feed, JsonSchema, Obj, Query, Tag } from '@dxos/echo';
|
|
13
|
+
import { Collection, View } from '@dxos/echo';
|
|
14
|
+
import { createFeedServiceLayer } from '@dxos/echo-db';
|
|
12
15
|
import { ClientPlugin } from '@dxos/plugin-client';
|
|
13
16
|
import { InboxPlugin } from '@dxos/plugin-inbox';
|
|
14
17
|
import { PreviewPlugin } from '@dxos/plugin-preview';
|
|
15
18
|
import { StorybookPlugin, corePlugins } from '@dxos/plugin-testing';
|
|
16
19
|
import { faker } from '@dxos/random';
|
|
17
20
|
import { useDatabase, useQuery } from '@dxos/react-client/echo';
|
|
18
|
-
import { withTheme } from '@dxos/react-ui/testing';
|
|
21
|
+
import { withLayout, withTheme } from '@dxos/react-ui/testing';
|
|
19
22
|
import { translations as stackTranslations } from '@dxos/react-ui-stack';
|
|
20
|
-
import {
|
|
21
|
-
import { Collection, View } from '@dxos/schema';
|
|
23
|
+
import { ViewModel } from '@dxos/schema';
|
|
22
24
|
import { createObjectFactory } from '@dxos/schema/testing';
|
|
23
25
|
import { Message, Organization, Person, Pipeline, Task } from '@dxos/types';
|
|
24
26
|
|
|
25
|
-
import { translations } from '
|
|
27
|
+
import { translations } from '../../translations';
|
|
28
|
+
import PipelineObjectSettings from '../PipelineObjectSettings';
|
|
26
29
|
|
|
27
30
|
import { PipelineContainer } from './PipelineContainer';
|
|
28
|
-
import { PipelineObjectSettings } from './PipelineSettings';
|
|
29
31
|
|
|
30
32
|
faker.seed(0);
|
|
31
33
|
|
|
@@ -39,18 +41,19 @@ const DefaultStory = () => {
|
|
|
39
41
|
}
|
|
40
42
|
|
|
41
43
|
return (
|
|
42
|
-
<
|
|
44
|
+
<div className='grow grid grid-cols-[1fr_350px] overflow-hidden h-full w-full'>
|
|
43
45
|
<PipelineContainer role='article' subject={pipeline} />
|
|
44
|
-
<PipelineObjectSettings pipeline={pipeline} classNames='border-
|
|
45
|
-
</
|
|
46
|
+
<PipelineObjectSettings pipeline={pipeline} classNames='border-s border-separator' />
|
|
47
|
+
</div>
|
|
46
48
|
);
|
|
47
49
|
};
|
|
48
50
|
|
|
49
51
|
const meta = {
|
|
50
|
-
title: 'plugins/plugin-pipeline/PipelineContainer',
|
|
52
|
+
title: 'plugins/plugin-pipeline/containers/PipelineContainer',
|
|
51
53
|
render: DefaultStory,
|
|
52
54
|
decorators: [
|
|
53
55
|
withTheme(),
|
|
56
|
+
withLayout({ layout: 'fullscreen' }),
|
|
54
57
|
withPluginManager({
|
|
55
58
|
plugins: [
|
|
56
59
|
...corePlugins(),
|
|
@@ -58,6 +61,7 @@ const meta = {
|
|
|
58
61
|
ClientPlugin({
|
|
59
62
|
types: [
|
|
60
63
|
Tag.Tag,
|
|
64
|
+
Feed.Feed,
|
|
61
65
|
Pipeline.Pipeline,
|
|
62
66
|
View.View,
|
|
63
67
|
Collection.Collection,
|
|
@@ -66,50 +70,71 @@ const meta = {
|
|
|
66
70
|
Person.Person,
|
|
67
71
|
Message.Message,
|
|
68
72
|
],
|
|
69
|
-
onClientInitialized: ({ client })
|
|
70
|
-
Effect.
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
const tag =
|
|
73
|
+
onClientInitialized: Effect.fnUntraced(function* ({ client }) {
|
|
74
|
+
yield* Effect.promise(() => client.halo.createIdentity());
|
|
75
|
+
yield* Effect.promise(() => client.spaces.waitUntilReady());
|
|
76
|
+
const space = client.spaces.default;
|
|
77
|
+
yield* Effect.promise(() => space.waitUntilReady());
|
|
78
|
+
|
|
79
|
+
yield* Effect.gen(function* () {
|
|
80
|
+
const tag = yield* Database.add(Tag.make({ label: 'important', hue: 'green' }));
|
|
77
81
|
const tagDxn = Obj.getDXN(tag).toString();
|
|
78
82
|
|
|
79
83
|
// Create a view for Contacts.
|
|
80
|
-
const personView =
|
|
84
|
+
const personView = ViewModel.make({
|
|
81
85
|
query: Query.select(Filter.type(Person.Person)),
|
|
82
|
-
jsonSchema:
|
|
86
|
+
jsonSchema: JsonSchema.toJsonSchema(Person.Person),
|
|
83
87
|
});
|
|
84
88
|
|
|
85
89
|
// Create a view for Organizations.
|
|
86
|
-
const organizationView =
|
|
90
|
+
const organizationView = ViewModel.make({
|
|
87
91
|
query: Query.select(Filter.type(Organization.Organization)).select(Filter.tag(tagDxn)),
|
|
88
|
-
jsonSchema:
|
|
92
|
+
jsonSchema: JsonSchema.toJsonSchema(Organization.Organization),
|
|
89
93
|
});
|
|
90
94
|
|
|
91
95
|
// Create a view for Tasks.
|
|
92
|
-
const taskView =
|
|
96
|
+
const taskView = ViewModel.make({
|
|
93
97
|
query: Query.select(Filter.type(Task.Task)).select(Filter.tag(tagDxn)),
|
|
94
|
-
jsonSchema:
|
|
98
|
+
jsonSchema: JsonSchema.toJsonSchema(Task.Task),
|
|
95
99
|
});
|
|
96
100
|
|
|
97
101
|
// Create a view for Project-Projects.
|
|
98
|
-
const projectView =
|
|
102
|
+
const projectView = ViewModel.make({
|
|
99
103
|
query: Query.select(Filter.type(Pipeline.Pipeline)),
|
|
100
|
-
jsonSchema:
|
|
104
|
+
jsonSchema: JsonSchema.toJsonSchema(Pipeline.Pipeline),
|
|
101
105
|
});
|
|
102
106
|
|
|
103
|
-
// Create a
|
|
104
|
-
const
|
|
105
|
-
const
|
|
106
|
-
|
|
107
|
-
|
|
107
|
+
// Create a feed for Messages.
|
|
108
|
+
const messageFeed = yield* Database.add(Feed.make({ name: 'Messages' }));
|
|
109
|
+
const messages = Array.from({ length: 20 }).map(() =>
|
|
110
|
+
Obj.make(Message.Message, {
|
|
111
|
+
created: faker.date.recent().toISOString(),
|
|
112
|
+
sender: { role: 'user' },
|
|
113
|
+
blocks: [{ _tag: 'text' as const, text: faker.lorem.sentences(2) }],
|
|
114
|
+
}),
|
|
115
|
+
);
|
|
116
|
+
yield* Feed.append(messageFeed, messages);
|
|
117
|
+
|
|
118
|
+
const messageQueueDxn = Feed.getQueueDxn(messageFeed)!.toString();
|
|
119
|
+
const messageView = ViewModel.make({
|
|
120
|
+
query: Query.select(Filter.type(Message.Message)).from({
|
|
121
|
+
queues: [messageQueueDxn],
|
|
108
122
|
}),
|
|
109
|
-
jsonSchema:
|
|
123
|
+
jsonSchema: JsonSchema.toJsonSchema(Message.Message),
|
|
110
124
|
});
|
|
111
125
|
|
|
112
|
-
// Create
|
|
126
|
+
// Create a feed for Tasks (for testing feed switching in settings).
|
|
127
|
+
const taskFeed = yield* Database.add(Feed.make({ name: 'Tasks' }));
|
|
128
|
+
const feedTasks = Array.from({ length: 10 }).map(() =>
|
|
129
|
+
Obj.make(Task.Task, {
|
|
130
|
+
title: faker.lorem.sentence(),
|
|
131
|
+
status: faker.helpers.arrayElement(['todo', 'in-progress', 'done']) as any,
|
|
132
|
+
priority: faker.helpers.arrayElement(['low', 'medium', 'high']) as any,
|
|
133
|
+
}),
|
|
134
|
+
);
|
|
135
|
+
yield* Feed.append(taskFeed, feedTasks);
|
|
136
|
+
|
|
137
|
+
// Create pipeline with columns.
|
|
113
138
|
const pipeline = Pipeline.make({
|
|
114
139
|
columns: [
|
|
115
140
|
{
|
|
@@ -139,75 +164,53 @@ const meta = {
|
|
|
139
164
|
},
|
|
140
165
|
],
|
|
141
166
|
});
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
167
|
+
yield* Database.add(pipeline);
|
|
168
|
+
|
|
169
|
+
// Generate sample Organizations.
|
|
170
|
+
for (const _ of Array.from({ length: 20 })) {
|
|
171
|
+
yield* Database.add(
|
|
172
|
+
Obj.make(Organization.Organization, {
|
|
173
|
+
[Obj.Meta]: {
|
|
174
|
+
tags: faker.datatype.boolean() ? [tagDxn] : [],
|
|
175
|
+
},
|
|
151
176
|
name: faker.company.name(),
|
|
152
177
|
website: faker.internet.url(),
|
|
153
178
|
description: faker.lorem.paragraph(),
|
|
154
179
|
image: faker.image.url(),
|
|
155
|
-
},
|
|
156
|
-
{
|
|
157
|
-
tags: faker.datatype.boolean() ? [Obj.getDXN(tag).toString()] : [],
|
|
158
|
-
},
|
|
180
|
+
}),
|
|
159
181
|
);
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Generate sample Tasks.
|
|
185
|
+
for (const _ of Array.from({ length: 20 })) {
|
|
186
|
+
yield* Database.add(
|
|
187
|
+
Obj.make(Task.Task, {
|
|
188
|
+
[Obj.Meta]: {
|
|
189
|
+
tags: faker.datatype.boolean() ? [tagDxn] : [],
|
|
190
|
+
},
|
|
168
191
|
title: faker.lorem.sentence(),
|
|
169
192
|
status: faker.helpers.arrayElement(['todo', 'in-progress', 'done']) as any,
|
|
170
193
|
priority: faker.helpers.arrayElement(['low', 'medium', 'high']) as any,
|
|
171
|
-
},
|
|
172
|
-
{
|
|
173
|
-
tags: faker.datatype.boolean() ? [Obj.getDXN(tag).toString()] : [],
|
|
174
|
-
},
|
|
194
|
+
}),
|
|
175
195
|
);
|
|
176
|
-
|
|
177
|
-
});
|
|
196
|
+
}
|
|
178
197
|
|
|
179
|
-
// Generate sample Contacts
|
|
198
|
+
// Generate sample Contacts.
|
|
180
199
|
const factory = createObjectFactory(space.db, faker as any);
|
|
181
200
|
yield* Effect.promise(() => factory([{ type: Person.Person, count: 12 }]));
|
|
182
201
|
|
|
183
|
-
// Generate sample Projects
|
|
184
|
-
Array.from({ length:
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
const message = Obj.make(Message.Message, {
|
|
195
|
-
created: faker.date.recent().toISOString(),
|
|
196
|
-
sender: { role: 'user' },
|
|
197
|
-
blocks: [
|
|
198
|
-
{
|
|
199
|
-
_tag: 'text' as const,
|
|
200
|
-
text: faker.lorem.sentences(2),
|
|
201
|
-
},
|
|
202
|
-
],
|
|
203
|
-
});
|
|
204
|
-
return message;
|
|
205
|
-
});
|
|
206
|
-
|
|
207
|
-
yield* Effect.promise(() => messageQueue.append(messages));
|
|
208
|
-
}),
|
|
202
|
+
// Generate sample Projects.
|
|
203
|
+
for (const _ of Array.from({ length: 20 })) {
|
|
204
|
+
yield* Database.add(
|
|
205
|
+
Pipeline.make({
|
|
206
|
+
name: faker.commerce.productName(),
|
|
207
|
+
description: faker.lorem.sentence(),
|
|
208
|
+
}),
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
}).pipe(Effect.provide(Layer.merge(Database.layer(space.db), createFeedServiceLayer(space.queues))));
|
|
212
|
+
}),
|
|
209
213
|
}),
|
|
210
|
-
|
|
211
214
|
InboxPlugin(),
|
|
212
215
|
PreviewPlugin(),
|
|
213
216
|
],
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2023 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import React, { useCallback, useEffect } from 'react';
|
|
6
|
+
|
|
7
|
+
import { Capabilities } from '@dxos/app-framework';
|
|
8
|
+
import { useCapability } from '@dxos/app-framework/ui';
|
|
9
|
+
import { useOperationInvoker } from '@dxos/app-framework/ui';
|
|
10
|
+
import { Surface } from '@dxos/app-framework/ui';
|
|
11
|
+
import {
|
|
12
|
+
OBJECT_ACTIONS_CONTRIBUTION_ID,
|
|
13
|
+
OBJECT_ACTIONS_CONTRIBUTION_PRIORITY,
|
|
14
|
+
type SurfaceComponentProps,
|
|
15
|
+
useObjectMenuItems,
|
|
16
|
+
} from '@dxos/app-toolkit/ui';
|
|
17
|
+
import { Obj } from '@dxos/echo';
|
|
18
|
+
import { ATTENDABLE_PATH_SEPARATOR, DeckOperation } from '@dxos/plugin-deck/types';
|
|
19
|
+
import { Panel } from '@dxos/react-ui';
|
|
20
|
+
import { useAttention } from '@dxos/react-ui-attention';
|
|
21
|
+
import { useMenu } from '@dxos/react-ui-menu';
|
|
22
|
+
import { type Pipeline } from '@dxos/types';
|
|
23
|
+
|
|
24
|
+
import { type ItemProps, PipelineComponent } from '../../components';
|
|
25
|
+
import { usePipelineBoardModel } from '../../hooks';
|
|
26
|
+
|
|
27
|
+
const PIPELINE_ITEM = 'PipelineItem';
|
|
28
|
+
|
|
29
|
+
export type PipelineContainerProps = SurfaceComponentProps<Pipeline.Pipeline>;
|
|
30
|
+
|
|
31
|
+
export const PipelineContainer = ({ role, subject: pipeline }: PipelineContainerProps) => {
|
|
32
|
+
const registry = useCapability(Capabilities.AtomRegistry);
|
|
33
|
+
const model = usePipelineBoardModel(pipeline, registry);
|
|
34
|
+
const { invokePromise } = useOperationInvoker();
|
|
35
|
+
const attendableId = Obj.getDXN(pipeline).toString();
|
|
36
|
+
const { hasAttention } = useAttention(attendableId);
|
|
37
|
+
|
|
38
|
+
const handleColumnAdd = useCallback(
|
|
39
|
+
() =>
|
|
40
|
+
invokePromise(DeckOperation.ChangeCompanion, {
|
|
41
|
+
companion: `${attendableId}${ATTENDABLE_PATH_SEPARATOR}settings`,
|
|
42
|
+
}),
|
|
43
|
+
[invokePromise, attendableId],
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
return (
|
|
47
|
+
<PipelineComponent.Root Item={PipelineItem} onAddColumn={handleColumnAdd}>
|
|
48
|
+
<Panel.Root role={role} className='dx-article'>
|
|
49
|
+
<Panel.Toolbar asChild>
|
|
50
|
+
<PipelineComponent.Toolbar disabled={!hasAttention} />
|
|
51
|
+
</Panel.Toolbar>
|
|
52
|
+
<Panel.Content asChild>
|
|
53
|
+
<PipelineComponent.Content model={model}>
|
|
54
|
+
<PipelineComponent.Columns pipeline={pipeline} />
|
|
55
|
+
</PipelineComponent.Content>
|
|
56
|
+
</Panel.Content>
|
|
57
|
+
</Panel.Root>
|
|
58
|
+
</PipelineComponent.Root>
|
|
59
|
+
);
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const PipelineItem = ({ item, projectionModel }: ItemProps) => {
|
|
63
|
+
const menu = useMenu(PIPELINE_ITEM);
|
|
64
|
+
const items = useObjectMenuItems(item);
|
|
65
|
+
|
|
66
|
+
useEffect(() => {
|
|
67
|
+
menu.addMenuItems({
|
|
68
|
+
id: OBJECT_ACTIONS_CONTRIBUTION_ID,
|
|
69
|
+
mode: 'additive',
|
|
70
|
+
priority: OBJECT_ACTIONS_CONTRIBUTION_PRIORITY,
|
|
71
|
+
items,
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
return () => menu.removeMenuItems(OBJECT_ACTIONS_CONTRIBUTION_ID);
|
|
75
|
+
}, [menu, items]);
|
|
76
|
+
|
|
77
|
+
return (
|
|
78
|
+
<Surface.Surface
|
|
79
|
+
role='card--content'
|
|
80
|
+
data={{
|
|
81
|
+
subject: item,
|
|
82
|
+
projection: projectionModel,
|
|
83
|
+
}}
|
|
84
|
+
limit={1}
|
|
85
|
+
/>
|
|
86
|
+
);
|
|
87
|
+
};
|
|
@@ -5,20 +5,20 @@
|
|
|
5
5
|
import * as Schema from 'effect/Schema';
|
|
6
6
|
import React, { useCallback, useMemo, useRef, useState } from 'react';
|
|
7
7
|
|
|
8
|
-
import { DXN, Filter, Obj, Query, type QueryAST, Ref, Tag
|
|
9
|
-
import { type Mutable } from '@dxos/echo/internal';
|
|
8
|
+
import { DXN, Filter, JsonSchema, Obj, Query, type QueryAST, Ref, Tag } from '@dxos/echo';
|
|
9
|
+
import { type JsonPath, type Mutable } from '@dxos/echo/internal';
|
|
10
10
|
import { useTypeOptions } from '@dxos/plugin-space';
|
|
11
11
|
import { resolveSchemaWithRegistry } from '@dxos/plugin-space';
|
|
12
|
-
import { getSpace, useQuery } from '@dxos/react-client/echo';
|
|
12
|
+
import { getSpace, useObject, useQuery } from '@dxos/react-client/echo';
|
|
13
13
|
import { IconButton, type ThemedClassName, useAsyncEffect, useTranslation } from '@dxos/react-ui';
|
|
14
14
|
import { Form, ViewEditor } from '@dxos/react-ui-form';
|
|
15
15
|
import { List } from '@dxos/react-ui-list';
|
|
16
|
-
import { type ProjectionModel,
|
|
16
|
+
import { type ProjectionModel, ViewModel } from '@dxos/schema';
|
|
17
17
|
import { Pipeline, Task } from '@dxos/types';
|
|
18
18
|
import { mx, osTranslations, subtleHover } from '@dxos/ui-theme';
|
|
19
19
|
import { arrayMove } from '@dxos/util';
|
|
20
20
|
|
|
21
|
-
import { meta } from '
|
|
21
|
+
import { meta } from '../../meta';
|
|
22
22
|
|
|
23
23
|
const listGrid = 'grid grid-cols-[min-content_1fr_min-content_min-content_min-content]';
|
|
24
24
|
const listItemGrid = 'grid grid-cols-subgrid col-span-5';
|
|
@@ -37,11 +37,12 @@ export const PipelineObjectSettings = ({ classNames, pipeline }: PipelineObjectS
|
|
|
37
37
|
const { t } = useTranslation(meta.id);
|
|
38
38
|
const space = getSpace(pipeline);
|
|
39
39
|
const [expandedId, setExpandedId] = useState<string>();
|
|
40
|
+
const [columns, updateColumns] = useObject(pipeline, 'columns');
|
|
40
41
|
const column = useMemo(
|
|
41
|
-
() =>
|
|
42
|
-
[
|
|
42
|
+
() => columns.find((column) => column.view.dxn.toString() === expandedId),
|
|
43
|
+
[columns, expandedId],
|
|
43
44
|
);
|
|
44
|
-
const view = column?.view
|
|
45
|
+
const [view, updateView] = useObject(column?.view);
|
|
45
46
|
const [schema, setSchema] = useState<Schema.Schema.AnyNoContext>(() => Schema.Struct({}));
|
|
46
47
|
const projectionRef = useRef<ProjectionModel>(null);
|
|
47
48
|
const tags = useQuery(space?.db, Filter.type(Tag.Tag));
|
|
@@ -67,10 +68,10 @@ export const PipelineObjectSettings = ({ classNames, pipeline }: PipelineObjectS
|
|
|
67
68
|
|
|
68
69
|
const handleMove = useCallback(
|
|
69
70
|
(fromIndex: number, toIndex: number) =>
|
|
70
|
-
|
|
71
|
-
arrayMove(
|
|
71
|
+
updateColumns((columns) => {
|
|
72
|
+
arrayMove(columns, fromIndex, toIndex);
|
|
72
73
|
}),
|
|
73
|
-
[
|
|
74
|
+
[updateColumns],
|
|
74
75
|
);
|
|
75
76
|
|
|
76
77
|
const handleQueryChanged = useCallback(
|
|
@@ -80,26 +81,42 @@ export const PipelineObjectSettings = ({ classNames, pipeline }: PipelineObjectS
|
|
|
80
81
|
}
|
|
81
82
|
|
|
82
83
|
const queue = target && DXN.tryParse(target) ? target : undefined;
|
|
83
|
-
const query = queue ? Query.fromAst(newQuery).
|
|
84
|
-
|
|
85
|
-
|
|
84
|
+
const query = queue ? Query.fromAst(newQuery).from({ queues: [queue] }) : Query.fromAst(newQuery);
|
|
85
|
+
updateView((view) => {
|
|
86
|
+
view.query.ast = query.ast as Mutable<typeof query.ast>;
|
|
86
87
|
});
|
|
87
88
|
const newSchema = await resolveSchemaWithRegistry(space.db.schemaRegistry, query.ast);
|
|
88
89
|
if (!newSchema) {
|
|
89
90
|
return;
|
|
90
91
|
}
|
|
91
92
|
|
|
92
|
-
const newView =
|
|
93
|
+
const newView = ViewModel.make({
|
|
93
94
|
query,
|
|
94
|
-
jsonSchema:
|
|
95
|
+
jsonSchema: JsonSchema.toJsonSchema(newSchema),
|
|
95
96
|
});
|
|
96
|
-
|
|
97
|
-
|
|
97
|
+
updateView((view) => {
|
|
98
|
+
view.projection = Obj.getSnapshot(newView).projection as Mutable<typeof view.projection>;
|
|
98
99
|
});
|
|
99
100
|
|
|
100
101
|
setSchema(() => newSchema);
|
|
101
102
|
},
|
|
102
|
-
[view, schema],
|
|
103
|
+
[view, updateView, schema],
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
const handleColumnValuesChanged = useCallback(
|
|
107
|
+
(column: Pipeline.Column) =>
|
|
108
|
+
(newValues: { name?: string }, { changed }: { changed: Record<JsonPath, boolean> }) => {
|
|
109
|
+
if (changed['name' as JsonPath]) {
|
|
110
|
+
const columnIndex = columns.findIndex((c) => c === column);
|
|
111
|
+
if (columnIndex === -1) {
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
updateColumns((cols) => {
|
|
115
|
+
cols[columnIndex].name = newValues.name ?? '';
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
},
|
|
119
|
+
[columns, updateColumns],
|
|
103
120
|
);
|
|
104
121
|
|
|
105
122
|
const handleToggleField = useCallback((column: Pipeline.Column) => {
|
|
@@ -114,50 +131,42 @@ export const PipelineObjectSettings = ({ classNames, pipeline }: PipelineObjectS
|
|
|
114
131
|
setExpandedId(undefined);
|
|
115
132
|
}
|
|
116
133
|
|
|
117
|
-
const index =
|
|
134
|
+
const index = columns.findIndex((l) => l === column);
|
|
118
135
|
const viewToRemove = await column.view.load();
|
|
119
|
-
|
|
120
|
-
|
|
136
|
+
updateColumns((columns) => {
|
|
137
|
+
columns.splice(index, 1);
|
|
121
138
|
});
|
|
122
139
|
space?.db.remove(viewToRemove);
|
|
123
140
|
},
|
|
124
|
-
[expandedId,
|
|
141
|
+
[expandedId, columns, updateColumns, space],
|
|
125
142
|
);
|
|
126
143
|
|
|
127
144
|
const handleAdd = useCallback(() => {
|
|
128
|
-
|
|
145
|
+
if (!space) {
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
const newView = ViewModel.make({
|
|
129
149
|
query: Query.select(Filter.type(Task.Task)),
|
|
130
|
-
jsonSchema:
|
|
150
|
+
jsonSchema: JsonSchema.toJsonSchema(Task.Task),
|
|
131
151
|
});
|
|
132
|
-
|
|
133
|
-
|
|
152
|
+
space.db.add(newView);
|
|
153
|
+
updateColumns((columns) => {
|
|
154
|
+
columns.push({
|
|
134
155
|
name: 'Tasks',
|
|
135
156
|
// Type assertion needed due to QueryAST type variance.
|
|
136
|
-
view: Ref.make(newView) as (typeof
|
|
157
|
+
view: Ref.make(newView) as (typeof columns)[number]['view'],
|
|
137
158
|
order: [],
|
|
138
159
|
});
|
|
139
160
|
});
|
|
140
161
|
setExpandedId(newView.id);
|
|
141
|
-
}, [
|
|
142
|
-
|
|
143
|
-
const handleColumnSave = useCallback(
|
|
144
|
-
(values: Schema.Schema.Type<typeof ColumnFormSchema>) => {
|
|
145
|
-
if (column) {
|
|
146
|
-
const columnIndex = pipeline.columns.findIndex((c) => c === column);
|
|
147
|
-
Obj.change(pipeline, (p) => {
|
|
148
|
-
p.columns[columnIndex].name = values.name;
|
|
149
|
-
});
|
|
150
|
-
}
|
|
151
|
-
},
|
|
152
|
-
[column, pipeline],
|
|
153
|
-
);
|
|
162
|
+
}, [space, updateColumns]);
|
|
154
163
|
|
|
155
164
|
return (
|
|
156
|
-
<div role='none' className={mx('
|
|
157
|
-
<h2 className='text-sm text-description
|
|
165
|
+
<div role='none' className={mx('py-form-padding overflow-y-auto', classNames)}>
|
|
166
|
+
<h2 className='text-sm text-description py-1'>{t('views label')}</h2>
|
|
158
167
|
|
|
159
168
|
<List.Root<Pipeline.Column>
|
|
160
|
-
items={
|
|
169
|
+
items={columns}
|
|
161
170
|
isItem={Schema.is(Pipeline.Column)}
|
|
162
171
|
getId={(column) => column.view.dxn.toString()}
|
|
163
172
|
onMove={handleMove}
|
|
@@ -172,7 +181,7 @@ export const PipelineObjectSettings = ({ classNames, pipeline }: PipelineObjectS
|
|
|
172
181
|
classNames={listItemGrid}
|
|
173
182
|
aria-expanded={expandedId === column.view.dxn.toString()}
|
|
174
183
|
>
|
|
175
|
-
<div role='none' className={mx(subtleHover, listItemGrid, 'rounded-
|
|
184
|
+
<div role='none' className={mx(subtleHover, listItemGrid, 'rounded-xs cursor-pointer min-h-10')}>
|
|
176
185
|
<List.ItemDragHandle />
|
|
177
186
|
<List.ItemTitle onClick={() => handleToggleField(column)}>{column.name}</List.ItemTitle>
|
|
178
187
|
<List.ItemDeleteButton
|
|
@@ -193,9 +202,13 @@ export const PipelineObjectSettings = ({ classNames, pipeline }: PipelineObjectS
|
|
|
193
202
|
onClick={() => handleToggleField(column)}
|
|
194
203
|
/>
|
|
195
204
|
</div>
|
|
196
|
-
{expandedId === column.view.dxn.toString() && view && (
|
|
197
|
-
<div role='none' className='col-span-5
|
|
198
|
-
<Form.Root
|
|
205
|
+
{expandedId === column.view.dxn.toString() && column?.view.target && (
|
|
206
|
+
<div role='none' className='col-span-5 my-2 border border-separator rounded-md'>
|
|
207
|
+
<Form.Root
|
|
208
|
+
schema={ColumnFormSchema}
|
|
209
|
+
values={column}
|
|
210
|
+
onValuesChanged={handleColumnValuesChanged(column)}
|
|
211
|
+
>
|
|
199
212
|
<Form.Content>
|
|
200
213
|
<Form.FieldSet />
|
|
201
214
|
</Form.Content>
|
|
@@ -205,8 +218,9 @@ export const PipelineObjectSettings = ({ classNames, pipeline }: PipelineObjectS
|
|
|
205
218
|
mode='tag'
|
|
206
219
|
readonly
|
|
207
220
|
schema={schema}
|
|
208
|
-
view={view}
|
|
221
|
+
view={column.view.target}
|
|
209
222
|
registry={space?.db.schemaRegistry}
|
|
223
|
+
db={space?.db}
|
|
210
224
|
tags={tags}
|
|
211
225
|
types={types}
|
|
212
226
|
onQueryChanged={handleQueryChanged}
|
|
@@ -220,11 +234,9 @@ export const PipelineObjectSettings = ({ classNames, pipeline }: PipelineObjectS
|
|
|
220
234
|
)}
|
|
221
235
|
</List.Root>
|
|
222
236
|
|
|
223
|
-
<div role='none' className='
|
|
224
|
-
<IconButton icon='ph--plus--regular' label={t('add view label')} onClick={handleAdd} classNames='
|
|
237
|
+
<div role='none' className='my-form-padding'>
|
|
238
|
+
<IconButton icon='ph--plus--regular' label={t('add view label')} onClick={handleAdd} classNames='w-full' />
|
|
225
239
|
</div>
|
|
226
240
|
</div>
|
|
227
241
|
);
|
|
228
242
|
};
|
|
229
|
-
|
|
230
|
-
export default PipelineObjectSettings;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2023 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { type ComponentType, lazy } from 'react';
|
|
6
|
+
|
|
7
|
+
export const PipelineContainer: ComponentType<any> = lazy(() => import('./PipelineContainer'));
|
|
8
|
+
export const PipelineObjectSettings: ComponentType<any> = lazy(() => import('./PipelineObjectSettings'));
|