@things-factory/ai-dashboard 0.0.0-stage → 10.1.213
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/client/bootstrap.ts +25 -0
- package/client/components/card-dialogs.ts +422 -0
- package/client/components/card-library.ts +272 -0
- package/client/components/dashboard-card.ts +450 -0
- package/client/components/proposal-preview.ts +96 -0
- package/client/index.ts +3 -0
- package/client/pages/my-dashboard-page.ts +371 -0
- package/client/route.ts +8 -0
- package/client/tsconfig.json +13 -0
- package/client/utils/card-api.ts +110 -0
- package/client/utils/card-words.ts +80 -0
- package/dist-client/bootstrap.d.ts +2 -0
- package/dist-client/bootstrap.js +24 -0
- package/dist-client/bootstrap.js.map +1 -0
- package/dist-client/components/card-dialogs.d.ts +59 -0
- package/dist-client/components/card-dialogs.js +457 -0
- package/dist-client/components/card-dialogs.js.map +1 -0
- package/dist-client/components/card-library.d.ts +23 -0
- package/dist-client/components/card-library.js +276 -0
- package/dist-client/components/card-library.js.map +1 -0
- package/dist-client/components/dashboard-card.d.ts +35 -0
- package/dist-client/components/dashboard-card.js +490 -0
- package/dist-client/components/dashboard-card.js.map +1 -0
- package/dist-client/components/proposal-preview.d.ts +17 -0
- package/dist-client/components/proposal-preview.js +111 -0
- package/dist-client/components/proposal-preview.js.map +1 -0
- package/dist-client/index.d.ts +3 -0
- package/dist-client/index.js +4 -0
- package/dist-client/index.js.map +1 -0
- package/dist-client/pages/my-dashboard-page.d.ts +46 -0
- package/dist-client/pages/my-dashboard-page.js +376 -0
- package/dist-client/pages/my-dashboard-page.js.map +1 -0
- package/dist-client/route.d.ts +1 -0
- package/dist-client/route.js +9 -0
- package/dist-client/route.js.map +1 -0
- package/dist-client/tsconfig.tsbuildinfo +1 -0
- package/dist-client/utils/card-api.d.ts +95 -0
- package/dist-client/utils/card-api.js +39 -0
- package/dist-client/utils/card-api.js.map +1 -0
- package/dist-client/utils/card-words.d.ts +24 -0
- package/dist-client/utils/card-words.js +72 -0
- package/dist-client/utils/card-words.js.map +1 -0
- package/dist-server/index.d.ts +11 -0
- package/dist-server/index.js +19 -0
- package/dist-server/index.js.map +1 -0
- package/dist-server/role-tasks.d.ts +2 -0
- package/dist-server/role-tasks.js +13 -0
- package/dist-server/role-tasks.js.map +1 -0
- package/dist-server/service/card/card-access.d.ts +32 -0
- package/dist-server/service/card/card-access.js +45 -0
- package/dist-server/service/card/card-access.js.map +1 -0
- package/dist-server/service/card/card-definition.d.ts +100 -0
- package/dist-server/service/card/card-definition.js +237 -0
- package/dist-server/service/card/card-definition.js.map +1 -0
- package/dist-server/service/card/card-names.d.ts +5 -0
- package/dist-server/service/card/card-names.js +9 -0
- package/dist-server/service/card/card-names.js.map +1 -0
- package/dist-server/service/card/card-proposal.d.ts +21 -0
- package/dist-server/service/card/card-proposal.js +28 -0
- package/dist-server/service/card/card-proposal.js.map +1 -0
- package/dist-server/service/card/card-resolver.d.ts +29 -0
- package/dist-server/service/card/card-resolver.js +541 -0
- package/dist-server/service/card/card-resolver.js.map +1 -0
- package/dist-server/service/card/card-run.d.ts +48 -0
- package/dist-server/service/card/card-run.js +239 -0
- package/dist-server/service/card/card-run.js.map +1 -0
- package/dist-server/service/card/card-tool.d.ts +218 -0
- package/dist-server/service/card/card-tool.js +313 -0
- package/dist-server/service/card/card-tool.js.map +1 -0
- package/dist-server/service/card/card-types.d.ts +56 -0
- package/dist-server/service/card/card-types.js +212 -0
- package/dist-server/service/card/card-types.js.map +1 -0
- package/dist-server/service/card/dashboard-card.d.ts +87 -0
- package/dist-server/service/card/dashboard-card.js +388 -0
- package/dist-server/service/card/dashboard-card.js.map +1 -0
- package/dist-server/service/card/long-text.d.ts +6 -0
- package/dist-server/service/card/long-text.js +28 -0
- package/dist-server/service/card/long-text.js.map +1 -0
- package/dist-server/service/index.d.ts +12 -0
- package/dist-server/service/index.js +24 -0
- package/dist-server/service/index.js.map +1 -0
- package/dist-server/tsconfig.tsbuildinfo +1 -0
- package/package.json +47 -4
- package/server/index.ts +16 -0
- package/server/role-tasks.ts +12 -0
- package/server/service/card/card-access.test.ts +138 -0
- package/server/service/card/card-access.ts +53 -0
- package/server/service/card/card-definition.ts +298 -0
- package/server/service/card/card-names.ts +5 -0
- package/server/service/card/card-proposal.ts +37 -0
- package/server/service/card/card-resolver.ts +434 -0
- package/server/service/card/card-run.test.ts +237 -0
- package/server/service/card/card-run.ts +288 -0
- package/server/service/card/card-tool.ts +299 -0
- package/server/service/card/card-types.ts +145 -0
- package/server/service/card/dashboard-card.ts +301 -0
- package/server/service/card/long-text.ts +25 -0
- package/server/service/index.ts +16 -0
- package/test/translations.test.ts +42 -0
- package/things-factory.config.js +8 -0
- package/translations/en.json +126 -0
- package/translations/es.json +126 -0
- package/translations/ja.json +126 -0
- package/translations/ko.json +126 -0
- package/translations/ms.json +126 -0
- package/translations/zh.json +126 -0
- package/tsconfig.json +9 -0
- package/README.md +0 -3
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* The conversation's dashboard card (ADR-0160 decision 9): its preview under the card, and pressing it saves the stored
|
|
3
|
+
* proposal to the person's dashboard. Registered here, by the package that declares the tool - the dock knows no kind.
|
|
4
|
+
*/
|
|
5
|
+
import { i18next } from '@operato/i18n'
|
|
6
|
+
import { registerProposalHandler, registerProposalView } from '@things-factory/ai-assistant/dist-client/index.js'
|
|
7
|
+
|
|
8
|
+
import './components/proposal-preview.js'
|
|
9
|
+
import { cardApi, dashboardChanged } from './utils/card-api.js'
|
|
10
|
+
|
|
11
|
+
const TOOL = 'proposeDashboardCard'
|
|
12
|
+
|
|
13
|
+
export default function bootstrap() {
|
|
14
|
+
registerProposalView(TOOL, { previewTag: 'ai-dashboard-proposal-preview', runLabelKey: 'ai-dashboard.preview.save', runLabel: 'Save to my dashboard' })
|
|
15
|
+
registerProposalHandler(TOOL, async (proposal, context) => {
|
|
16
|
+
if (!context.sessionId || !proposal?.key) return { verdict: 'unavailable', errorCode: 'no-conversation' }
|
|
17
|
+
const saved = await cardApi.saveProposal(context.sessionId, proposal.key)
|
|
18
|
+
dashboardChanged()
|
|
19
|
+
return {
|
|
20
|
+
verdict: 'accepted',
|
|
21
|
+
applied: 1,
|
|
22
|
+
note: String(i18next.t(saved.version > 1 ? 'ai-dashboard.preview.saved-version' : 'ai-dashboard.preview.saved', { name: proposal.card?.name ?? '', version: saved.version }))
|
|
23
|
+
}
|
|
24
|
+
})
|
|
25
|
+
}
|
|
@@ -0,0 +1,422 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* The small forms a card opens - what to change with the assistant, a new name, whom to share it with, and its
|
|
3
|
+
* versions (ADR-0160 decisions 7, 9). Each changes one card through its own door and tells an open dashboard.
|
|
4
|
+
*/
|
|
5
|
+
import '@material/web/icon/icon.js'
|
|
6
|
+
import '@material/web/button/filled-button.js'
|
|
7
|
+
import '@material/web/button/text-button.js'
|
|
8
|
+
|
|
9
|
+
import { LitElement, css, html, nothing } from 'lit'
|
|
10
|
+
import { customElement, property, state } from 'lit/decorators.js'
|
|
11
|
+
|
|
12
|
+
import { i18next, localize } from '@operato/i18n'
|
|
13
|
+
import { notify, openPopup } from '@operato/layout'
|
|
14
|
+
import { askAssistant } from '@things-factory/ai-assistant/dist-client/index.js'
|
|
15
|
+
|
|
16
|
+
import { cardApi, dashboardChanged, type CardItem, type CardShare } from '../utils/card-api.js'
|
|
17
|
+
|
|
18
|
+
const t = (key: string, options: Record<string, unknown> = {}) => String(i18next.t(key, options))
|
|
19
|
+
|
|
20
|
+
const FormStyles = css`
|
|
21
|
+
:host {
|
|
22
|
+
display: flex;
|
|
23
|
+
flex-direction: column;
|
|
24
|
+
gap: 14px;
|
|
25
|
+
padding: 16px 20px 20px;
|
|
26
|
+
color: var(--ui-ink, var(--md-sys-color-on-surface));
|
|
27
|
+
font-size: var(--ui-t-body, 13px);
|
|
28
|
+
line-height: 1.5;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
p.lead {
|
|
32
|
+
margin: 0;
|
|
33
|
+
color: var(--ui-muted, var(--md-sys-color-on-surface-variant));
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
textarea,
|
|
37
|
+
input[type='text'],
|
|
38
|
+
input[type='search'] {
|
|
39
|
+
box-sizing: border-box;
|
|
40
|
+
width: 100%;
|
|
41
|
+
padding: 9px 12px;
|
|
42
|
+
border: 1px solid var(--ui-line, var(--md-sys-color-outline-variant));
|
|
43
|
+
border-radius: 8px;
|
|
44
|
+
background: var(--ui-panel, var(--md-sys-color-surface-container-lowest));
|
|
45
|
+
color: inherit;
|
|
46
|
+
font: inherit;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
textarea {
|
|
50
|
+
min-height: 88px;
|
|
51
|
+
resize: vertical;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
textarea:focus,
|
|
55
|
+
input:focus {
|
|
56
|
+
outline: 2px solid color-mix(in srgb, var(--ui-accent, var(--md-sys-color-primary)) 40%, transparent);
|
|
57
|
+
border-color: var(--ui-accent, var(--md-sys-color-primary));
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.hints {
|
|
61
|
+
display: flex;
|
|
62
|
+
flex-wrap: wrap;
|
|
63
|
+
gap: 6px;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.hints button {
|
|
67
|
+
padding: 4px 10px;
|
|
68
|
+
border: 1px solid var(--ui-line, var(--md-sys-color-outline-variant));
|
|
69
|
+
border-radius: 999px;
|
|
70
|
+
background: transparent;
|
|
71
|
+
color: var(--ui-muted, var(--md-sys-color-on-surface-variant));
|
|
72
|
+
font: inherit;
|
|
73
|
+
font-size: var(--ui-t-small, 12px);
|
|
74
|
+
cursor: pointer;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.hints button:hover {
|
|
78
|
+
border-color: var(--ui-accent, var(--md-sys-color-primary));
|
|
79
|
+
color: var(--ui-accent, var(--md-sys-color-primary));
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.actions {
|
|
83
|
+
display: flex;
|
|
84
|
+
justify-content: flex-end;
|
|
85
|
+
gap: 8px;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
h5 {
|
|
89
|
+
margin: 6px 0 0;
|
|
90
|
+
font-size: var(--ui-t-small, 12px);
|
|
91
|
+
font-weight: 600;
|
|
92
|
+
color: var(--ui-muted, var(--md-sys-color-on-surface-variant));
|
|
93
|
+
text-transform: none;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
label.option {
|
|
97
|
+
display: flex;
|
|
98
|
+
align-items: center;
|
|
99
|
+
gap: 10px;
|
|
100
|
+
padding: 8px 10px;
|
|
101
|
+
border: 1px solid var(--ui-line-soft, var(--md-sys-color-outline-variant));
|
|
102
|
+
border-radius: 8px;
|
|
103
|
+
cursor: pointer;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
label.option small {
|
|
107
|
+
margin-left: auto;
|
|
108
|
+
color: var(--ui-faint, var(--md-sys-color-on-surface-variant));
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
.people {
|
|
112
|
+
display: flex;
|
|
113
|
+
flex-direction: column;
|
|
114
|
+
gap: 6px;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
.person {
|
|
118
|
+
display: flex;
|
|
119
|
+
align-items: center;
|
|
120
|
+
gap: 8px;
|
|
121
|
+
padding: 6px 10px;
|
|
122
|
+
border-radius: 8px;
|
|
123
|
+
background: var(--ui-panel-2, var(--md-sys-color-surface-container-low));
|
|
124
|
+
--md-icon-size: 16px;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
.person small {
|
|
128
|
+
color: var(--ui-faint, var(--md-sys-color-on-surface-variant));
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
.person button,
|
|
132
|
+
.found button {
|
|
133
|
+
margin-left: auto;
|
|
134
|
+
border: 0;
|
|
135
|
+
background: transparent;
|
|
136
|
+
color: var(--ui-accent, var(--md-sys-color-primary));
|
|
137
|
+
font: inherit;
|
|
138
|
+
cursor: pointer;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
.found {
|
|
142
|
+
display: flex;
|
|
143
|
+
flex-direction: column;
|
|
144
|
+
border: 1px solid var(--ui-line-soft, var(--md-sys-color-outline-variant));
|
|
145
|
+
border-radius: 8px;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
.found div {
|
|
149
|
+
display: flex;
|
|
150
|
+
align-items: center;
|
|
151
|
+
gap: 8px;
|
|
152
|
+
padding: 6px 10px;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
.found div + div {
|
|
156
|
+
border-top: 1px solid var(--ui-line-soft, var(--md-sys-color-outline-variant));
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
.note {
|
|
160
|
+
margin: 0;
|
|
161
|
+
font-size: var(--ui-t-small, 12px);
|
|
162
|
+
color: var(--ui-faint, var(--md-sys-color-on-surface-variant));
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
ol.versions {
|
|
166
|
+
list-style: none;
|
|
167
|
+
margin: 0;
|
|
168
|
+
padding: 0;
|
|
169
|
+
display: flex;
|
|
170
|
+
flex-direction: column;
|
|
171
|
+
gap: 6px;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
ol.versions li {
|
|
175
|
+
display: flex;
|
|
176
|
+
align-items: center;
|
|
177
|
+
gap: 12px;
|
|
178
|
+
padding: 10px 12px;
|
|
179
|
+
border: 1px solid var(--ui-line-soft, var(--md-sys-color-outline-variant));
|
|
180
|
+
border-radius: 8px;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
ol.versions .v {
|
|
184
|
+
flex: none;
|
|
185
|
+
min-width: 36px;
|
|
186
|
+
font-weight: 650;
|
|
187
|
+
font-variant-numeric: tabular-nums;
|
|
188
|
+
color: var(--ui-accent, var(--md-sys-color-primary));
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
ol.versions .what {
|
|
192
|
+
flex: 1;
|
|
193
|
+
min-width: 0;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
ol.versions small {
|
|
197
|
+
display: block;
|
|
198
|
+
color: var(--ui-faint, var(--md-sys-color-on-surface-variant));
|
|
199
|
+
}
|
|
200
|
+
`
|
|
201
|
+
|
|
202
|
+
/** What to change in a card - sent to the conversation beside the page with the card picked, as a screen request. */
|
|
203
|
+
@customElement('ai-dashboard-card-ai-form')
|
|
204
|
+
export class CardAiForm extends localize(i18next)(LitElement) {
|
|
205
|
+
static styles = FormStyles
|
|
206
|
+
@property({ attribute: false }) card!: CardItem
|
|
207
|
+
@state() private text = ''
|
|
208
|
+
|
|
209
|
+
render() {
|
|
210
|
+
const hints = ['ai-dashboard.ai.hint-period', 'ai-dashboard.ai.hint-top', 'ai-dashboard.ai.hint-line', 'ai-dashboard.ai.hint-table']
|
|
211
|
+
return html`<p class="lead">${t('ai-dashboard.ai.lead', { name: this.card.name })}</p>
|
|
212
|
+
<textarea .value=${this.text} placeholder=${t('ai-dashboard.ai.placeholder')} @input=${(e: any) => (this.text = e.target.value)} @keydown=${(e: KeyboardEvent) => e.key === 'Enter' && (e.metaKey || e.ctrlKey) && this.send()}></textarea>
|
|
213
|
+
<div class="hints">${hints.map(key => html`<button @click=${() => (this.text = t(key))}>${t(key)}</button>`)}</div>
|
|
214
|
+
<div class="actions">
|
|
215
|
+
<md-text-button @click=${() => this.close()}>${t('ai-dashboard.button.cancel')}</md-text-button>
|
|
216
|
+
<md-filled-button ?disabled=${!this.text.trim()} @click=${() => this.send()}>${t('ai-dashboard.ai.send')}</md-filled-button>
|
|
217
|
+
</div>`
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
private close() {
|
|
221
|
+
this.dispatchEvent(new CustomEvent('done', { bubbles: true, composed: true }))
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
private send() {
|
|
225
|
+
if (!this.text.trim()) return
|
|
226
|
+
/* Closed first: the dock opens over the page, and a popup that is no longer the top one refuses to close. */
|
|
227
|
+
this.close()
|
|
228
|
+
askAssistant({
|
|
229
|
+
ask: t('ai-dashboard.ai.ask', { name: this.card.name, version: this.card.version, change: this.text.trim() }),
|
|
230
|
+
selection: { title: t('ai-dashboard.title'), list: 'dashboardCards', records: [{ id: this.card.id, label: this.card.name }] }
|
|
231
|
+
})
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/** A card's new name - a new version. */
|
|
236
|
+
@customElement('ai-dashboard-card-rename-form')
|
|
237
|
+
export class CardRenameForm extends localize(i18next)(LitElement) {
|
|
238
|
+
static styles = FormStyles
|
|
239
|
+
@property({ attribute: false }) card!: CardItem
|
|
240
|
+
@state() private name = ''
|
|
241
|
+
|
|
242
|
+
connectedCallback() {
|
|
243
|
+
super.connectedCallback()
|
|
244
|
+
this.name = this.card?.name ?? ''
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
render() {
|
|
248
|
+
return html`<input type="text" .value=${this.name} maxlength="100" @input=${(e: any) => (this.name = e.target.value)} @keydown=${(e: KeyboardEvent) => e.key === 'Enter' && this.save()} />
|
|
249
|
+
<div class="actions">
|
|
250
|
+
<md-text-button @click=${() => this.close()}>${t('ai-dashboard.button.cancel')}</md-text-button>
|
|
251
|
+
<md-filled-button ?disabled=${!this.name.trim() || this.name.trim() === this.card.name} @click=${() => this.save()}>${t('ai-dashboard.button.save')}</md-filled-button>
|
|
252
|
+
</div>`
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
private close() {
|
|
256
|
+
this.dispatchEvent(new CustomEvent('done', { bubbles: true, composed: true }))
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
private async save() {
|
|
260
|
+
try {
|
|
261
|
+
await cardApi.rename(this.card.id, this.name.trim())
|
|
262
|
+
dashboardChanged()
|
|
263
|
+
this.close()
|
|
264
|
+
} catch (error: any) {
|
|
265
|
+
notify({ level: 'error', message: error?.message ?? String(error) })
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/** Whom one's own card is shared with - the whole domain, one's own departments, people found by name. */
|
|
271
|
+
@customElement('ai-dashboard-card-share-form')
|
|
272
|
+
export class CardShareForm extends localize(i18next)(LitElement) {
|
|
273
|
+
static styles = FormStyles
|
|
274
|
+
@property({ attribute: false }) card!: CardItem
|
|
275
|
+
@state() private options?: Awaited<ReturnType<typeof cardApi.shareOptions>>
|
|
276
|
+
@state() private shares: CardShare[] = []
|
|
277
|
+
@state() private found: { id: string; name: string; note?: string }[] = []
|
|
278
|
+
@state() private search = ''
|
|
279
|
+
private searching?: ReturnType<typeof setTimeout>
|
|
280
|
+
|
|
281
|
+
async connectedCallback() {
|
|
282
|
+
super.connectedCallback()
|
|
283
|
+
this.shares = [...(this.card?.shares ?? [])].map(({ scope, targetId, targetName }) => ({ scope, targetId, targetName }))
|
|
284
|
+
try {
|
|
285
|
+
this.options = await cardApi.shareOptions()
|
|
286
|
+
} catch (error: any) {
|
|
287
|
+
notify({ level: 'error', message: error?.message ?? String(error) })
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
private has(scope: string, id: string) {
|
|
292
|
+
return this.shares.some(one => one.scope === scope && one.targetId === id)
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
private toggle(share: CardShare, on: boolean) {
|
|
296
|
+
this.shares = on ? [...this.shares.filter(one => !(one.scope === share.scope && one.targetId === share.targetId)), share] : this.shares.filter(one => !(one.scope === share.scope && one.targetId === share.targetId))
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
private find(text: string) {
|
|
300
|
+
this.search = text
|
|
301
|
+
clearTimeout(this.searching)
|
|
302
|
+
this.searching = setTimeout(async () => {
|
|
303
|
+
try {
|
|
304
|
+
this.found = text.trim() ? await cardApi.people(text.trim()) : []
|
|
305
|
+
} catch {
|
|
306
|
+
this.found = []
|
|
307
|
+
}
|
|
308
|
+
}, 250)
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
render() {
|
|
312
|
+
const options = this.options
|
|
313
|
+
const people = this.shares.filter(one => one.scope === 'USER')
|
|
314
|
+
return html`<p class="lead">${t('ai-dashboard.share.lead')}</p>
|
|
315
|
+
${options
|
|
316
|
+
? html`<h5>${t('ai-dashboard.share.everyone-title')}</h5>
|
|
317
|
+
<label class="option">
|
|
318
|
+
<input type="checkbox" .checked=${this.has('DOMAIN', options.domainId)} @change=${(e: any) => this.toggle({ scope: 'DOMAIN', targetId: options.domainId, targetName: options.domainName }, e.target.checked)} />
|
|
319
|
+
<md-icon>domain</md-icon>${t('ai-dashboard.share.everyone-in', { name: options.domainName })}
|
|
320
|
+
</label>
|
|
321
|
+
${options.departmentsAvailable && options.departments.length
|
|
322
|
+
? html`<h5>${t('ai-dashboard.share.departments')}</h5>
|
|
323
|
+
${options.departments.map(
|
|
324
|
+
one => html`<label class="option">
|
|
325
|
+
<input type="checkbox" .checked=${this.has('DEPARTMENT', one.id)} @change=${(e: any) => this.toggle({ scope: 'DEPARTMENT', targetId: one.id, targetName: one.name }, e.target.checked)} />
|
|
326
|
+
<md-icon>groups</md-icon>${one.name}<small>${t('ai-dashboard.share.department-members')}</small>
|
|
327
|
+
</label>`
|
|
328
|
+
)}`
|
|
329
|
+
: nothing}
|
|
330
|
+
<h5>${t('ai-dashboard.share.people')}</h5>
|
|
331
|
+
${people.length
|
|
332
|
+
? html`<div class="people">
|
|
333
|
+
${people.map(one => html`<div class="person"><md-icon>person</md-icon>${one.targetName ?? one.targetId}<button @click=${() => this.toggle(one, false)}>${t('ai-dashboard.share.remove')}</button></div>`)}
|
|
334
|
+
</div>`
|
|
335
|
+
: nothing}
|
|
336
|
+
${options.peopleAvailable
|
|
337
|
+
? html`<input type="search" .value=${this.search} placeholder=${t('ai-dashboard.share.find-person')} @input=${(e: any) => this.find(e.target.value)} />
|
|
338
|
+
${this.found.length
|
|
339
|
+
? html`<div class="found">
|
|
340
|
+
${this.found
|
|
341
|
+
.filter(one => !this.has('USER', one.id))
|
|
342
|
+
.map(one => html`<div><md-icon>person_add</md-icon>${one.name} ${one.note ? html`<small>${one.note}</small>` : nothing}<button @click=${() => this.toggle({ scope: 'USER', targetId: one.id, targetName: one.name }, true)}>${t('ai-dashboard.share.add')}</button></div>`)}
|
|
343
|
+
</div>`
|
|
344
|
+
: nothing}`
|
|
345
|
+
: html`<p class="note">${t('ai-dashboard.share.people-unavailable')}</p>`}
|
|
346
|
+
<p class="note">${t('ai-dashboard.share.rule')}</p>`
|
|
347
|
+
: nothing}
|
|
348
|
+
<div class="actions">
|
|
349
|
+
<md-text-button @click=${() => this.close()}>${t('ai-dashboard.button.cancel')}</md-text-button>
|
|
350
|
+
<md-filled-button ?disabled=${!options} @click=${() => this.save()}>${t('ai-dashboard.button.save')}</md-filled-button>
|
|
351
|
+
</div>`
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
private close() {
|
|
355
|
+
this.dispatchEvent(new CustomEvent('done', { bubbles: true, composed: true }))
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
private async save() {
|
|
359
|
+
try {
|
|
360
|
+
await cardApi.share(this.card.id, this.shares)
|
|
361
|
+
notify({ level: 'info', message: t(this.shares.length ? 'ai-dashboard.text.shared' : 'ai-dashboard.text.private', { name: this.card.name }) })
|
|
362
|
+
dashboardChanged()
|
|
363
|
+
this.close()
|
|
364
|
+
} catch (error: any) {
|
|
365
|
+
notify({ level: 'error', message: error?.message ?? String(error) })
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/** One's own card's versions, newest first - an earlier one comes back as a new version. */
|
|
371
|
+
@customElement('ai-dashboard-card-versions')
|
|
372
|
+
export class CardVersions extends localize(i18next)(LitElement) {
|
|
373
|
+
static styles = FormStyles
|
|
374
|
+
@property({ attribute: false }) card!: CardItem
|
|
375
|
+
@state() private versions?: Awaited<ReturnType<typeof cardApi.versions>>
|
|
376
|
+
|
|
377
|
+
async connectedCallback() {
|
|
378
|
+
super.connectedCallback()
|
|
379
|
+
try {
|
|
380
|
+
this.versions = await cardApi.versions(this.card.id)
|
|
381
|
+
} catch (error: any) {
|
|
382
|
+
notify({ level: 'error', message: error?.message ?? String(error) })
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
render() {
|
|
387
|
+
const view = (definition: any) => t(`ai-dashboard.view.${definition?.view?.kind ?? 'table'}`)
|
|
388
|
+
return html`<p class="lead">${t('ai-dashboard.versions.lead')}</p>
|
|
389
|
+
<ol class="versions">
|
|
390
|
+
${(this.versions ?? []).map(
|
|
391
|
+
one => html`<li>
|
|
392
|
+
<span class="v">v${one.version}</span>
|
|
393
|
+
<span class="what">${one.name}<small>${view(one.definition)} · ${one.creatorName ?? ''} · ${one.createdAt ? new Date(one.createdAt).toLocaleString(i18next.language) : ''}</small></span>
|
|
394
|
+
${one.version === this.card.version
|
|
395
|
+
? html`<small>${t('ai-dashboard.versions.current')}</small>`
|
|
396
|
+
: html`<md-text-button @click=${() => this.restore(one.version)}>${t('ai-dashboard.versions.restore')}</md-text-button>`}
|
|
397
|
+
</li>`
|
|
398
|
+
)}
|
|
399
|
+
</ol>`
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
private async restore(version: number) {
|
|
403
|
+
try {
|
|
404
|
+
await cardApi.restore(this.card.id, version)
|
|
405
|
+
notify({ level: 'info', message: t('ai-dashboard.text.restored', { version }) })
|
|
406
|
+
dashboardChanged()
|
|
407
|
+
this.dispatchEvent(new CustomEvent('done', { bubbles: true, composed: true }))
|
|
408
|
+
} catch (error: any) {
|
|
409
|
+
notify({ level: 'error', message: error?.message ?? String(error) })
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
function open(template: unknown, title: string, size: 'small' | 'medium' = 'small') {
|
|
415
|
+
const popup: any = openPopup(html`<div @done=${() => popup?.close?.()}>${template}</div>`, { backdrop: true, size, title })
|
|
416
|
+
return popup
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
export const openCardAiPrompt = (card: CardItem) => open(html`<ai-dashboard-card-ai-form .card=${card}></ai-dashboard-card-ai-form>`, t('ai-dashboard.card.ai'), 'medium')
|
|
420
|
+
export const openCardRename = (card: CardItem) => open(html`<ai-dashboard-card-rename-form .card=${card}></ai-dashboard-card-rename-form>`, t('ai-dashboard.card.rename'))
|
|
421
|
+
export const openCardShare = (card: CardItem) => open(html`<ai-dashboard-card-share-form .card=${card}></ai-dashboard-card-share-form>`, t('ai-dashboard.share.title', { name: card.name }), 'medium')
|
|
422
|
+
export const openCardVersions = (card: CardItem) => open(html`<ai-dashboard-card-versions .card=${card}></ai-dashboard-card-versions>`, t('ai-dashboard.card.versions'), 'medium')
|