@things-factory/ai-dashboard 10.1.255 → 10.1.257

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.
@@ -7,15 +7,17 @@ import '@material/web/button/filled-button.js'
7
7
  import '@material/web/button/text-button.js'
8
8
  import '@operato/popup/ox-popup-actions.js'
9
9
 
10
+ import gql from 'graphql-tag'
10
11
  import { LitElement, css, html, nothing } from 'lit'
11
12
  import { customElement, property, state } from 'lit/decorators.js'
12
13
 
14
+ import { QueryController } from '@operato/graphql'
13
15
  import { i18next, localize } from '@operato/i18n'
14
16
  import { notify, openPopup } from '@operato/layout'
15
17
  import { CommonPopupStyles, PopupBodyStyles } from '@operato/styles'
16
18
  import { askAssistant } from '@things-factory/ai-assistant/dist-client/index.js'
17
19
 
18
- import { cardApi, dashboardChanged, type CardItem, type CardShare } from '../utils/card-api.js'
20
+ import { cardApi, dashboardChanged, type CardItem, type CardShare, type CardVersion, type ShareOptions } from '../utils/card-api.js'
19
21
  import { cardSelection } from '../utils/card-words.js'
20
22
 
21
23
  const t = (key: string, options: Record<string, unknown> = {}) => String(i18next.t(key, options))
@@ -270,20 +272,31 @@ export class CardRenameForm extends localize(i18next)(LitElement) {
270
272
  export class CardShareForm extends localize(i18next)(LitElement) {
271
273
  static styles = [CommonPopupStyles, PopupBodyStyles, FormStyles]
272
274
  @property({ attribute: false }) card!: CardItem
273
- @state() private options?: Awaited<ReturnType<typeof cardApi.shareOptions>>
274
275
  @state() private shares: CardShare[] = []
275
- @state() private found: { id: string; name: string; note?: string }[] = []
276
276
  @state() private search = ''
277
+ /** The name the people are found by - the typed text once typing pauses. */
278
+ @state() private findBy = ''
277
279
  private searching?: ReturnType<typeof setTimeout>
278
280
 
279
- async connectedCallback() {
281
+ private optionsRead = new QueryController(
282
+ this,
283
+ gql`query { dashboardShareOptions { domainId domainName departments { id name } peopleAvailable roles { id name note } } }`
284
+ )
285
+ private peopleRead = new QueryController(this, gql`query ($text: String!) { dashboardSharePeople(text: $text) { id name note } }`, {
286
+ variables: () => (this.findBy ? { text: this.findBy } : undefined)
287
+ })
288
+
289
+ private get options(): ShareOptions | undefined {
290
+ return this.optionsRead.data?.dashboardShareOptions
291
+ }
292
+
293
+ private get found(): { id: string; name: string; note?: string }[] {
294
+ return this.peopleRead.data?.dashboardSharePeople ?? []
295
+ }
296
+
297
+ connectedCallback() {
280
298
  super.connectedCallback()
281
299
  this.shares = [...(this.card?.shares ?? [])].map(({ scope, targetId, targetName }) => ({ scope, targetId, targetName }))
282
- try {
283
- this.options = await cardApi.shareOptions()
284
- } catch (error: any) {
285
- notify({ level: 'error', message: error?.message ?? String(error) })
286
- }
287
300
  }
288
301
 
289
302
  private has(scope: string, id: string) {
@@ -297,13 +310,7 @@ export class CardShareForm extends localize(i18next)(LitElement) {
297
310
  private find(text: string) {
298
311
  this.search = text
299
312
  clearTimeout(this.searching)
300
- this.searching = setTimeout(async () => {
301
- try {
302
- this.found = text.trim() ? await cardApi.people(text.trim()) : []
303
- } catch {
304
- this.found = []
305
- }
306
- }, 250)
313
+ this.searching = setTimeout(() => (this.findBy = text.trim()), 250)
307
314
  }
308
315
 
309
316
  render() {
@@ -381,15 +388,15 @@ export class CardShareForm extends localize(i18next)(LitElement) {
381
388
  export class CardVersions extends localize(i18next)(LitElement) {
382
389
  static styles = [CommonPopupStyles, PopupBodyStyles, FormStyles]
383
390
  @property({ attribute: false }) card!: CardItem
384
- @state() private versions?: Awaited<ReturnType<typeof cardApi.versions>>
385
391
 
386
- async connectedCallback() {
387
- super.connectedCallback()
388
- try {
389
- this.versions = await cardApi.versions(this.card.id)
390
- } catch (error: any) {
391
- notify({ level: 'error', message: error?.message ?? String(error) })
392
- }
392
+ private versionsRead = new QueryController(
393
+ this,
394
+ gql`query ($id: String!) { dashboardCardVersions(id: $id) { version name definition creatorName createdAt } }`,
395
+ { variables: () => (this.card ? { id: this.card.id } : undefined) }
396
+ )
397
+
398
+ private get versions(): CardVersion[] | undefined {
399
+ return this.versionsRead.data?.dashboardCardVersions
393
400
  }
394
401
 
395
402
  render() {
@@ -6,15 +6,17 @@ import '@material/web/icon/icon.js'
6
6
  import '@material/web/button/filled-tonal-button.js'
7
7
  import '@material/web/button/text-button.js'
8
8
 
9
+ import gql from 'graphql-tag'
9
10
  import { LitElement, css, html, nothing } from 'lit'
10
11
  import { customElement, state } from 'lit/decorators.js'
11
12
 
13
+ import { QueryController } from '@operato/graphql'
12
14
  import { i18next, localize } from '@operato/i18n'
13
15
  import { ScrollbarStyles } from '@operato/styles'
14
16
  import { notify } from '@operato/layout'
15
17
  import { OxPrompt } from '@operato/popup/ox-prompt.js'
16
18
 
17
- import { cardApi, DASHBOARD_CHANGED, dashboardChanged, type CardItem } from '../utils/card-api.js'
19
+ import { cardApi, CARD_FIELDS, DASHBOARD_CHANGED, dashboardChanged, type CardItem } from '../utils/card-api.js'
18
20
  import { ownershipLine, viewIcon } from '../utils/card-words.js'
19
21
  import { openCardRename, openCardShare, openCardVersions } from './card-dialogs.js'
20
22
 
@@ -179,15 +181,28 @@ export class CardLibrary extends localize(i18next)(LitElement) {
179
181
  ]
180
182
 
181
183
  @state() private which: 'mine' | 'shared' = 'mine'
182
- @state() private cards?: CardItem[]
183
- @state() private error?: string
184
184
 
185
- private onChanged = () => this.load()
185
+ /* The tab's cards (ADR-0190); a change told from elsewhere reads them again. */
186
+ private library = new QueryController(
187
+ this,
188
+ gql`query ($which: String!) { dashboardCards(which: $which) { ${CARD_FIELDS} } }`,
189
+ { variables: () => ({ which: this.which }) }
190
+ )
191
+
192
+ private get cards(): CardItem[] | undefined {
193
+ return this.library.data?.dashboardCards
194
+ }
195
+
196
+ private get error(): string | undefined {
197
+ const error = this.library.error as any
198
+ return error ? (error.message ?? String(error)) : undefined
199
+ }
200
+
201
+ private onChanged = () => this.library.refetch()
186
202
 
187
203
  connectedCallback() {
188
204
  super.connectedCallback()
189
205
  window.addEventListener(DASHBOARD_CHANGED, this.onChanged)
190
- this.load()
191
206
  }
192
207
 
193
208
  disconnectedCallback() {
@@ -195,19 +210,8 @@ export class CardLibrary extends localize(i18next)(LitElement) {
195
210
  super.disconnectedCallback()
196
211
  }
197
212
 
198
- private async load() {
199
- try {
200
- this.cards = await cardApi.library(this.which)
201
- this.error = undefined
202
- } catch (error: any) {
203
- this.error = error?.message ?? String(error)
204
- }
205
- }
206
-
207
213
  private show(which: 'mine' | 'shared') {
208
214
  this.which = which
209
- this.cards = undefined
210
- this.load()
211
215
  }
212
216
 
213
217
  render() {
@@ -7,13 +7,15 @@ import '@material/web/icon/icon.js'
7
7
  import '@things-factory/ai-assistant/dist-client/index.js'
8
8
 
9
9
  import { LitElement, css, html, nothing } from 'lit'
10
- import { customElement, property, state } from 'lit/decorators.js'
10
+ import { customElement, property } from 'lit/decorators.js'
11
11
 
12
+ import gql from 'graphql-tag'
13
+ import { QueryController } from '@operato/graphql'
12
14
  import { i18next, localize } from '@operato/i18n'
13
15
  import '@operato/popup/ox-popup-menu.js'
14
16
  import { OxPopupMenu } from '@operato/popup/ox-popup-menu.js'
15
17
 
16
- import { cardApi, type CardItem, type CardRun } from '../utils/card-api.js'
18
+ import { type CardItem, type CardRun } from '../utils/card-api.js'
17
19
  import { drawsNothing, issueLine, ownershipLine, partialLine, readLine, viewIcon } from '../utils/card-words.js'
18
20
 
19
21
  const t = (key: string, options: Record<string, unknown> = {}) => String(i18next.t(key, options))
@@ -280,36 +282,39 @@ export class AiDashboardCard extends localize(i18next)(LitElement) {
280
282
  @property({ type: Boolean }) first = false
281
283
  @property({ type: Boolean }) last = false
282
284
 
283
- @state() private run?: CardRun
284
- @state() private failed?: string
285
- @state() private loading = false
286
- @state() private readAt?: Date
285
+ /* The card drawn from the data - the server runs its definition as the viewer (ADR-0190). */
286
+ private cardRun = new QueryController(
287
+ this,
288
+ gql`
289
+ query ($id: String!) {
290
+ dashboardCardRun(id: $id)
291
+ }
292
+ `,
293
+ { variables: () => (this.card ? { id: this.card.id } : undefined) }
294
+ )
295
+
296
+ private get run(): CardRun | undefined {
297
+ return this.cardRun.data?.dashboardCardRun
298
+ }
287
299
 
288
- private readingFor?: string
300
+ private get failed() {
301
+ const error = this.cardRun.error as any
302
+ return error ? (error.message ?? String(error)) : undefined
303
+ }
304
+
305
+ private get loading() {
306
+ return this.cardRun.loading
307
+ }
289
308
 
290
309
  updated(changes: Map<string, unknown>) {
291
- /* A new card, or a new version of it, is read again; nothing else is. */
292
- const key = this.card ? `${this.card.id}@${this.card.version}` : undefined
293
- if (changes.has('card') && key && key !== this.readingFor) this.refresh()
310
+ /* A new version of the same card is read again; a new card is read by its own variables. */
311
+ const old = changes.get('card') as CardItem | undefined
312
+ if (old && this.card && old.id === this.card.id && old.version !== this.card.version) this.refresh()
294
313
  }
295
314
 
296
- /** Draws the card again from the data - the server runs its definition as the viewer. */
297
- async refresh() {
298
- if (!this.card) return
299
- const key = `${this.card.id}@${this.card.version}`
300
- this.readingFor = key
301
- this.loading = true
302
- this.failed = undefined
303
- try {
304
- const run = await cardApi.run(this.card.id)
305
- if (this.readingFor !== key) return
306
- this.run = run
307
- this.readAt = new Date()
308
- } catch (error: any) {
309
- if (this.readingFor === key) this.failed = error?.message ?? String(error)
310
- } finally {
311
- if (this.readingFor === key) this.loading = false
312
- }
315
+ /** Draws the card again from the data. */
316
+ refresh() {
317
+ return this.cardRun.refetch()
313
318
  }
314
319
 
315
320
  private act(action: CardAction) {
@@ -384,7 +389,7 @@ export class AiDashboardCard extends localize(i18next)(LitElement) {
384
389
  private renderFooter() {
385
390
  const partial = partialLine(this.run!, t)
386
391
  return html`<footer>
387
- <span>${readLine(this.run!, this.readAt ?? new Date(), t, i18next.language)}</span>
392
+ <span>${readLine(this.run!, this.cardRun.answeredAt ?? new Date(), t, i18next.language)}</span>
388
393
  ${partial ? html`<span class="partial"><md-icon>warning</md-icon>${partial}</span>` : nothing}
389
394
  </footer>`
390
395
  }
@@ -6,12 +6,14 @@
6
6
  import '@material/web/icon/icon.js'
7
7
  import '@things-factory/ai-assistant/dist-client/index.js'
8
8
 
9
+ import gql from 'graphql-tag'
9
10
  import { LitElement, css, html, nothing } from 'lit'
10
- import { customElement, property, state } from 'lit/decorators.js'
11
+ import { customElement, property } from 'lit/decorators.js'
11
12
 
13
+ import { QueryController } from '@operato/graphql'
12
14
  import { i18next, localize } from '@operato/i18n'
13
15
 
14
- import { cardApi, type CardRun } from '../utils/card-api.js'
16
+ import { type CardRun } from '../utils/card-api.js'
15
17
  import { drawsNothing, issueLine, partialLine } from '../utils/card-words.js'
16
18
 
17
19
  const t = (key: string, options: Record<string, unknown> = {}) => String(i18next.t(key, options))
@@ -59,23 +61,20 @@ export class ProposalPreview extends localize(i18next)(LitElement) {
59
61
  @property({ attribute: false }) sessionId?: string
60
62
  @property({ attribute: false }) proposalKey?: string
61
63
 
62
- @state() private run?: CardRun
63
- @state() private failed?: string
64
- private readFor?: string
64
+ /* The proposal run as the viewer, for the proposal shown (ADR-0190). */
65
+ private preview = new QueryController(
66
+ this,
67
+ gql`query ($s: String!, $k: String!) { dashboardCardProposalPreview(sessionId: $s, proposalKey: $k) }`,
68
+ { variables: () => (this.sessionId && this.proposalKey ? { s: this.sessionId, k: this.proposalKey } : undefined) }
69
+ )
65
70
 
66
- updated() {
67
- const key = this.sessionId && this.proposalKey ? `${this.sessionId}/${this.proposalKey}` : undefined
68
- if (key && key !== this.readFor) this.read(key)
71
+ private get run(): CardRun | undefined {
72
+ return this.preview.data?.dashboardCardProposalPreview
69
73
  }
70
74
 
71
- private async read(key: string) {
72
- this.readFor = key
73
- try {
74
- const run = await cardApi.preview(this.sessionId!, this.proposalKey!)
75
- if (this.readFor === key) this.run = run
76
- } catch (error: any) {
77
- if (this.readFor === key) this.failed = error?.message ?? String(error)
78
- }
75
+ private get failed(): string | undefined {
76
+ const error = this.preview.error as any
77
+ return error ? (error.message ?? String(error)) : undefined
79
78
  }
80
79
 
81
80
  render() {
@@ -9,16 +9,18 @@ import '@material/web/icon/icon.js'
9
9
  import '../components/dashboard-card.js'
10
10
  import '../components/card-library.js'
11
11
 
12
+ import gql from 'graphql-tag'
12
13
  import { css, html, nothing } from 'lit'
13
14
  import { customElement, state } from 'lit/decorators.js'
14
15
 
16
+ import { MutationController, QueryController } from '@operato/graphql'
15
17
  import { i18next, localize } from '@operato/i18n'
16
18
  import { notify, openPopup } from '@operato/layout'
17
19
  import { PageView } from '@operato/shell'
18
20
  import { DocumentPageStyles, ScrollbarStyles } from '@operato/styles'
19
21
  import { askAssistant } from '@things-factory/ai-assistant/dist-client/index.js'
20
22
 
21
- import { cardApi, DASHBOARD_CHANGED, dashboardChanged, type CardItem, type PlaceItem } from '../utils/card-api.js'
23
+ import { cardApi, CARD_FIELDS, DASHBOARD_CHANGED, dashboardChanged, type CardItem, type PlaceItem } from '../utils/card-api.js'
22
24
  import { movedPlaces } from '../utils/card-words.js'
23
25
  import { dashboardExamples } from '../utils/examples.js'
24
26
  import { openCardAiPrompt, openCardRename, openCardShare, openCardVersions } from '../components/card-dialogs.js'
@@ -199,12 +201,33 @@ export class MyDashboardPage extends localize(i18next)(PageView) {
199
201
  `
200
202
  ]
201
203
 
202
- @state() private places: PlaceItem[] = []
203
- @state() private loaded = false
204
- @state() private error?: string
205
204
  @state() private editing = false
205
+ /** The places as moved here, shown while the move is being saved. */
206
+ @state() private arranged?: PlaceItem[]
207
+
208
+ /* A page refresh, or a change told from elsewhere, reads the board again. */
209
+ private board = new QueryController(this, gql`query { myDashboard { id order width height card { ${CARD_FIELDS} } } }`)
210
+ private arranging = new MutationController(this, gql`mutation ($places: [DashboardPlaceInput!]!) { arrangeDashboard(places: $places) }`, {
211
+ refetch: () => [this.board]
212
+ })
213
+ private unplacing = new MutationController(this, gql`mutation ($id: String!) { removeDashboardPlace(id: $id) }`, {
214
+ refetch: () => [this.board]
215
+ })
216
+
217
+ private get places(): PlaceItem[] {
218
+ return (this.arranging.pending && this.arranged) || this.board.data?.myDashboard || []
219
+ }
220
+
221
+ private get loaded() {
222
+ return !!this.board.answeredAt
223
+ }
224
+
225
+ private get error(): string | undefined {
226
+ const error = this.board.error as any
227
+ return error ? (error.message ?? String(error)) : undefined
228
+ }
206
229
 
207
- private onChanged = () => this.load()
230
+ private onChanged = () => this.board.refetch()
208
231
 
209
232
  get context() {
210
233
  return {
@@ -233,22 +256,6 @@ export class MyDashboardPage extends localize(i18next)(PageView) {
233
256
  if (changes.has('editing')) this.updateContext()
234
257
  }
235
258
 
236
- /* Not awaited: the shell sets the tab's title only after this returns. */
237
- async pageUpdated(changes: any) {
238
- if ((this as any).active && changes.active) this.load()
239
- }
240
-
241
- private async load() {
242
- try {
243
- this.places = await cardApi.myDashboard()
244
- this.error = undefined
245
- } catch (error: any) {
246
- this.error = error?.message ?? String(error)
247
- } finally {
248
- this.loaded = true
249
- }
250
- }
251
-
252
259
  render() {
253
260
  return html`<div class="page">
254
261
  ${this.error
@@ -319,15 +326,10 @@ export class MyDashboardPage extends localize(i18next)(PageView) {
319
326
  return this.places.find(one => one.card.id === card.id)
320
327
  }
321
328
 
329
+ /* The board shows the move at once; a failed one goes back to the board read. */
322
330
  private async arrange(next: PlaceItem[]) {
323
- const before = this.places
324
- this.places = next
325
- try {
326
- await cardApi.arrange(next.map(({ id, order, width, height }) => ({ id, order, width, height })))
327
- } catch (error: any) {
328
- this.places = before
329
- notify({ level: 'error', message: error?.message ?? String(error) })
330
- }
331
+ this.arranged = next
332
+ await this.arranging.run({ places: next.map(({ id, order, width, height }) => ({ id, order, width, height })) })
331
333
  }
332
334
 
333
335
  private async onCardAction(event: CustomEvent) {
@@ -349,9 +351,8 @@ export class MyDashboardPage extends localize(i18next)(PageView) {
349
351
  return dashboardChanged()
350
352
  case 'unplace':
351
353
  if (!place) return
352
- await cardApi.unplace(place.id)
353
- notify({ level: 'info', message: t('ai-dashboard.text.unplaced', { name: card.name }) })
354
- return this.load()
354
+ if (await this.unplacing.run({ id: place.id })) notify({ level: 'info', message: t('ai-dashboard.text.unplaced', { name: card.name }) })
355
+ return
355
356
  case 'left':
356
357
  case 'right':
357
358
  return place && this.arrange(movedPlaces(this.places, place.id, action === 'left' ? -1 : 1))
@@ -1,6 +1,6 @@
1
1
  /*
2
- * The doors the dashboard screens call (server: card-resolver.ts). One place, so the page, the library and the
3
- * conversation's card read the same fields.
2
+ * The dashboard's changes (server: card-resolver.ts), and the card fields the screens read - one place, so the page,
3
+ * the library and the conversation's card read the same fields. Each screen declares its own reads (ADR-0190).
4
4
  */
5
5
  import gql from 'graphql-tag'
6
6
 
@@ -50,56 +50,45 @@ export interface CardRun {
50
50
  source?: { key: string; labelKey?: string }
51
51
  }
52
52
 
53
- const CARD_FIELDS = `id name description version definition access ownerName copiedFromId placed shares { scope targetId targetName } updatedAt`
53
+ export interface ShareOptions {
54
+ domainId: string
55
+ domainName: string
56
+ departments: { id: string; name: string }[]
57
+ peopleAvailable: boolean
58
+ roles: { id: string; name: string; note?: string | null }[]
59
+ }
54
60
 
55
- async function ask<T>(document: string, variables: Record<string, unknown> = {}, mutate = false): Promise<T> {
56
- const response: any = mutate
57
- ? await client.mutate({ mutation: gql(document), variables })
58
- : await client.query({ query: gql(document), variables, fetchPolicy: 'no-cache' })
61
+ export interface CardVersion {
62
+ version: number
63
+ name: string
64
+ definition: any
65
+ creatorName?: string
66
+ createdAt?: string
67
+ }
68
+
69
+ export const CARD_FIELDS = `id name description version definition access ownerName copiedFromId placed shares { scope targetId targetName } updatedAt`
70
+
71
+ async function ask<T>(document: string, variables: Record<string, unknown> = {}): Promise<T> {
72
+ const response: any = await client.mutate({ mutation: gql(document), variables })
59
73
  if (response.errors?.length) throw new Error(response.errors[0].message)
60
74
  return response.data
61
75
  }
62
76
 
63
77
  export const cardApi = {
64
- myDashboard: async () => (await ask<any>(`query { myDashboard { id order width height card { ${CARD_FIELDS} } } }`)).myDashboard as PlaceItem[],
65
- library: async (which: 'mine' | 'shared') => (await ask<any>(`query ($which: String!) { dashboardCards(which: $which) { ${CARD_FIELDS} } }`, { which })).dashboardCards as CardItem[],
66
- run: async (id: string) => (await ask<any>(`query ($id: String!) { dashboardCardRun(id: $id) }`, { id })).dashboardCardRun as CardRun,
67
- preview: async (sessionId: string, proposalKey: string) =>
68
- (await ask<any>(`query ($s: String!, $k: String!) { dashboardCardProposalPreview(sessionId: $s, proposalKey: $k) }`, { s: sessionId, k: proposalKey })).dashboardCardProposalPreview as CardRun,
69
- versions: async (id: string) =>
70
- (await ask<any>(`query ($id: String!) { dashboardCardVersions(id: $id) { version name definition creatorName createdAt } }`, { id })).dashboardCardVersions as {
71
- version: number
72
- name: string
73
- definition: any
74
- creatorName?: string
75
- createdAt?: string
76
- }[],
77
- shareOptions: async () =>
78
- (await ask<any>(`query { dashboardShareOptions { domainId domainName departments { id name } peopleAvailable roles { id name note } } }`)).dashboardShareOptions as {
79
- domainId: string
80
- domainName: string
81
- departments: { id: string; name: string }[]
82
- peopleAvailable: boolean
83
- roles: { id: string; name: string; note?: string | null }[]
84
- },
85
- people: async (text: string) => (await ask<any>(`query ($text: String!) { dashboardSharePeople(text: $text) { id name note } }`, { text })).dashboardSharePeople as { id: string; name: string; note?: string }[],
86
78
  saveProposal: async (sessionId: string, proposalKey: string) =>
87
- (await ask<any>(`mutation ($s: String!, $k: String!) { saveDashboardCardProposal(sessionId: $s, proposalKey: $k) }`, { s: sessionId, k: proposalKey }, true)).saveDashboardCardProposal as {
79
+ (await ask<any>(`mutation ($s: String!, $k: String!) { saveDashboardCardProposal(sessionId: $s, proposalKey: $k) }`, { s: sessionId, k: proposalKey })).saveDashboardCardProposal as {
88
80
  cardId: string
89
81
  version: number
90
82
  placeId: string
91
83
  again?: boolean
92
84
  },
93
- rename: async (id: string, name: string) => (await ask<any>(`mutation ($id: String!, $name: String!) { renameDashboardCard(id: $id, name: $name) { id } }`, { id, name }, true)).renameDashboardCard,
94
- restore: async (id: string, version: number) => (await ask<any>(`mutation ($id: String!, $v: Int!) { restoreDashboardCardVersion(id: $id, version: $v) { id } }`, { id, v: version }, true)).restoreDashboardCardVersion,
95
- copy: async (id: string) => (await ask<any>(`mutation ($id: String!) { copyDashboardCard(id: $id) { id } }`, { id }, true)).copyDashboardCard,
96
- remove: async (id: string) => (await ask<any>(`mutation ($id: String!) { deleteDashboardCard(id: $id) }`, { id }, true)).deleteDashboardCard,
85
+ rename: async (id: string, name: string) => (await ask<any>(`mutation ($id: String!, $name: String!) { renameDashboardCard(id: $id, name: $name) { id } }`, { id, name })).renameDashboardCard,
86
+ restore: async (id: string, version: number) => (await ask<any>(`mutation ($id: String!, $v: Int!) { restoreDashboardCardVersion(id: $id, version: $v) { id } }`, { id, v: version })).restoreDashboardCardVersion,
87
+ copy: async (id: string) => (await ask<any>(`mutation ($id: String!) { copyDashboardCard(id: $id) { id } }`, { id })).copyDashboardCard,
88
+ remove: async (id: string) => (await ask<any>(`mutation ($id: String!) { deleteDashboardCard(id: $id) }`, { id })).deleteDashboardCard,
97
89
  share: async (id: string, shares: CardShare[]) =>
98
- (await ask<any>(`mutation ($id: String!, $shares: [DashboardCardShareInput!]!) { shareDashboardCard(id: $id, shares: $shares) { ${CARD_FIELDS} } }`, { id, shares }, true)).shareDashboardCard as CardItem,
99
- place: async (cardId: string) => (await ask<any>(`mutation ($id: String!) { placeDashboardCard(cardId: $id) { id } }`, { id: cardId }, true)).placeDashboardCard,
100
- unplace: async (id: string) => (await ask<any>(`mutation ($id: String!) { removeDashboardPlace(id: $id) }`, { id }, true)).removeDashboardPlace,
101
- arrange: async (places: { id: string; order: number; width: number; height: number }[]) =>
102
- (await ask<any>(`mutation ($places: [DashboardPlaceInput!]!) { arrangeDashboard(places: $places) }`, { places }, true)).arrangeDashboard
90
+ (await ask<any>(`mutation ($id: String!, $shares: [DashboardCardShareInput!]!) { shareDashboardCard(id: $id, shares: $shares) { ${CARD_FIELDS} } }`, { id, shares })).shareDashboardCard as CardItem,
91
+ place: async (cardId: string) => (await ask<any>(`mutation ($id: String!) { placeDashboardCard(cardId: $id) { id } }`, { id: cardId })).placeDashboardCard
103
92
  }
104
93
 
105
94
  /** Told when a card is saved, copied, placed or removed elsewhere - an open dashboard reads itself again. */
@@ -30,12 +30,16 @@ declare const CardShareForm_base: (new (...args: any[]) => LitElement) & typeof
30
30
  export declare class CardShareForm extends CardShareForm_base {
31
31
  static styles: import("lit").CSSResult[];
32
32
  card: CardItem;
33
- private options?;
34
33
  private shares;
35
- private found;
36
34
  private search;
35
+ /** The name the people are found by - the typed text once typing pauses. */
36
+ private findBy;
37
37
  private searching?;
38
- connectedCallback(): Promise<void>;
38
+ private optionsRead;
39
+ private peopleRead;
40
+ private get options();
41
+ private get found();
42
+ connectedCallback(): void;
39
43
  private has;
40
44
  private toggle;
41
45
  private find;
@@ -48,8 +52,8 @@ declare const CardVersions_base: (new (...args: any[]) => LitElement) & typeof L
48
52
  export declare class CardVersions extends CardVersions_base {
49
53
  static styles: import("lit").CSSResult[];
50
54
  card: CardItem;
51
- private versions?;
52
- connectedCallback(): Promise<void>;
55
+ private versionsRead;
56
+ private get versions();
53
57
  render(): import("lit-html").TemplateResult<1>;
54
58
  private restore;
55
59
  }
@@ -7,8 +7,10 @@ import '@material/web/icon/icon.js';
7
7
  import '@material/web/button/filled-button.js';
8
8
  import '@material/web/button/text-button.js';
9
9
  import '@operato/popup/ox-popup-actions.js';
10
+ import gql from 'graphql-tag';
10
11
  import { LitElement, css, html, nothing } from 'lit';
11
12
  import { customElement, property, state } from 'lit/decorators.js';
13
+ import { QueryController } from '@operato/graphql';
12
14
  import { i18next, localize } from '@operato/i18n';
13
15
  import { notify, openPopup } from '@operato/layout';
14
16
  import { CommonPopupStyles, PopupBodyStyles } from '@operato/styles';
@@ -284,19 +286,24 @@ let CardShareForm = class CardShareForm extends localize(i18next)(LitElement) {
284
286
  constructor() {
285
287
  super(...arguments);
286
288
  this.shares = [];
287
- this.found = [];
288
289
  this.search = '';
290
+ /** The name the people are found by - the typed text once typing pauses. */
291
+ this.findBy = '';
292
+ this.optionsRead = new QueryController(this, gql `query { dashboardShareOptions { domainId domainName departments { id name } peopleAvailable roles { id name note } } }`);
293
+ this.peopleRead = new QueryController(this, gql `query ($text: String!) { dashboardSharePeople(text: $text) { id name note } }`, {
294
+ variables: () => (this.findBy ? { text: this.findBy } : undefined)
295
+ });
289
296
  }
290
297
  static { this.styles = [CommonPopupStyles, PopupBodyStyles, FormStyles]; }
291
- async connectedCallback() {
298
+ get options() {
299
+ return this.optionsRead.data?.dashboardShareOptions;
300
+ }
301
+ get found() {
302
+ return this.peopleRead.data?.dashboardSharePeople ?? [];
303
+ }
304
+ connectedCallback() {
292
305
  super.connectedCallback();
293
306
  this.shares = [...(this.card?.shares ?? [])].map(({ scope, targetId, targetName }) => ({ scope, targetId, targetName }));
294
- try {
295
- this.options = await cardApi.shareOptions();
296
- }
297
- catch (error) {
298
- notify({ level: 'error', message: error?.message ?? String(error) });
299
- }
300
307
  }
301
308
  has(scope, id) {
302
309
  return this.shares.some(one => one.scope === scope && one.targetId === id);
@@ -307,14 +314,7 @@ let CardShareForm = class CardShareForm extends localize(i18next)(LitElement) {
307
314
  find(text) {
308
315
  this.search = text;
309
316
  clearTimeout(this.searching);
310
- this.searching = setTimeout(async () => {
311
- try {
312
- this.found = text.trim() ? await cardApi.people(text.trim()) : [];
313
- }
314
- catch {
315
- this.found = [];
316
- }
317
- }, 250);
317
+ this.searching = setTimeout(() => (this.findBy = text.trim()), 250);
318
318
  }
319
319
  render() {
320
320
  const options = this.options;
@@ -384,37 +384,31 @@ __decorate([
384
384
  property({ attribute: false }),
385
385
  __metadata("design:type", Object)
386
386
  ], CardShareForm.prototype, "card", void 0);
387
- __decorate([
388
- state(),
389
- __metadata("design:type", Object)
390
- ], CardShareForm.prototype, "options", void 0);
391
387
  __decorate([
392
388
  state(),
393
389
  __metadata("design:type", Array)
394
390
  ], CardShareForm.prototype, "shares", void 0);
395
391
  __decorate([
396
392
  state(),
397
- __metadata("design:type", Array)
398
- ], CardShareForm.prototype, "found", void 0);
393
+ __metadata("design:type", Object)
394
+ ], CardShareForm.prototype, "search", void 0);
399
395
  __decorate([
400
396
  state(),
401
397
  __metadata("design:type", Object)
402
- ], CardShareForm.prototype, "search", void 0);
398
+ ], CardShareForm.prototype, "findBy", void 0);
403
399
  CardShareForm = __decorate([
404
400
  customElement('ai-dashboard-card-share-form')
405
401
  ], CardShareForm);
406
402
  export { CardShareForm };
407
403
  /** One's own card's versions, newest first - an earlier one comes back as a new version. */
408
404
  let CardVersions = class CardVersions extends localize(i18next)(LitElement) {
405
+ constructor() {
406
+ super(...arguments);
407
+ this.versionsRead = new QueryController(this, gql `query ($id: String!) { dashboardCardVersions(id: $id) { version name definition creatorName createdAt } }`, { variables: () => (this.card ? { id: this.card.id } : undefined) });
408
+ }
409
409
  static { this.styles = [CommonPopupStyles, PopupBodyStyles, FormStyles]; }
410
- async connectedCallback() {
411
- super.connectedCallback();
412
- try {
413
- this.versions = await cardApi.versions(this.card.id);
414
- }
415
- catch (error) {
416
- notify({ level: 'error', message: error?.message ?? String(error) });
417
- }
410
+ get versions() {
411
+ return this.versionsRead.data?.dashboardCardVersions;
418
412
  }
419
413
  render() {
420
414
  const view = (definition) => t(`ai-dashboard.view.${definition?.view?.kind ?? 'table'}`);
@@ -447,10 +441,6 @@ __decorate([
447
441
  property({ attribute: false }),
448
442
  __metadata("design:type", Object)
449
443
  ], CardVersions.prototype, "card", void 0);
450
- __decorate([
451
- state(),
452
- __metadata("design:type", Object)
453
- ], CardVersions.prototype, "versions", void 0);
454
444
  CardVersions = __decorate([
455
445
  customElement('ai-dashboard-card-versions')
456
446
  ], CardVersions);