@crombolo/backoffice-host 0.12.1 → 0.13.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +2 -2
- package/plugin/assistant/umbraco-package.json +1 -1
- package/plugin/branding/bundle-builder-labels.js +30 -1
- package/plugin/branding/sign-in.js +31 -0
- package/plugin/bundles-created-overview.js +31 -18
- package/plugin/bundles-installed.js +132 -53
- package/plugin/bundles-marketplace.js +12 -2
- package/plugin/changes-dashboard.js +8 -1
- package/plugin/download.js +19 -0
- package/plugin/form-picker.js +1 -1
- package/plugin/forms-client.js +5 -0
- package/plugin/forms-entries.js +30 -16
- package/plugin/forms-overview.js +55 -33
- package/plugin/localizations/umbraco-package.json +1 -1
- package/plugin/seo/umbraco-package.json +1 -1
- package/plugin/umbraco-package.json +109 -3
- package/plugin/workflow-client.js +40 -0
- package/plugin/workflow-designer.js +565 -0
- package/plugin/workflow-graph.js +132 -0
- package/plugin/workflow-requests.js +164 -0
- package/plugin/workflow-tab.js +219 -0
package/plugin/forms-overview.js
CHANGED
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
import '@umbraco-cms/backoffice/document'
|
|
15
15
|
import { css, html, nothing, repeat } from '@umbraco-cms/backoffice/external/lit'
|
|
16
16
|
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'
|
|
17
|
+
import { UMB_NOTIFICATION_CONTEXT } from '@umbraco-cms/backoffice/notification'
|
|
17
18
|
import {
|
|
18
19
|
deleteForm,
|
|
19
20
|
listForms,
|
|
@@ -61,8 +62,13 @@ export default class CromboloFormsOverviewElement extends UmbLitElement {
|
|
|
61
62
|
_types: { state: true },
|
|
62
63
|
}
|
|
63
64
|
|
|
65
|
+
#notifications
|
|
66
|
+
|
|
64
67
|
constructor() {
|
|
65
68
|
super()
|
|
69
|
+
this.consumeContext(UMB_NOTIFICATION_CONTEXT, (context) => {
|
|
70
|
+
this.#notifications = context
|
|
71
|
+
})
|
|
66
72
|
this._forms = undefined
|
|
67
73
|
this._editing = undefined
|
|
68
74
|
this._problems = []
|
|
@@ -490,16 +496,19 @@ export default class CromboloFormsOverviewElement extends UmbLitElement {
|
|
|
490
496
|
try {
|
|
491
497
|
const result = await saveForm(this._editing)
|
|
492
498
|
if (result.ok) {
|
|
493
|
-
|
|
499
|
+
// The green toast every workspace shows on a save.
|
|
500
|
+
this.#notifications?.peek('positive', { data: { message: 'Form saved' } })
|
|
494
501
|
this._editing = undefined
|
|
495
502
|
await this.#load()
|
|
496
503
|
} else {
|
|
497
504
|
this._problems = result.body.problems ?? []
|
|
498
|
-
if (result.body.status === 'readOnly')
|
|
499
|
-
this.
|
|
505
|
+
if (result.body.status === 'readOnly' && this._problems.length === 0)
|
|
506
|
+
this._problems = [
|
|
507
|
+
{ path: '', message: 'The schema directory is read-only on this node.' },
|
|
508
|
+
]
|
|
500
509
|
}
|
|
501
510
|
} catch (error) {
|
|
502
|
-
this.
|
|
511
|
+
this._problems = [{ path: '', message: error.message }]
|
|
503
512
|
} finally {
|
|
504
513
|
this._busy = false
|
|
505
514
|
}
|
|
@@ -517,20 +526,19 @@ export default class CromboloFormsOverviewElement extends UmbLitElement {
|
|
|
517
526
|
}
|
|
518
527
|
|
|
519
528
|
render() {
|
|
520
|
-
if (this._forms === undefined) return html`<uui-loader></uui-loader>`
|
|
521
|
-
return
|
|
529
|
+
if (this._forms === undefined) return html`<div class="list"><uui-loader></uui-loader></div>`
|
|
530
|
+
if (this._editing) return this.#editor()
|
|
531
|
+
return html`<div class="list">
|
|
522
532
|
${this._notice ? html`<uui-box><p>${this._notice}</p></uui-box>` : nothing}
|
|
523
|
-
${this
|
|
524
|
-
|
|
533
|
+
${this.#list()}
|
|
534
|
+
</div>`
|
|
525
535
|
}
|
|
526
536
|
|
|
527
537
|
#list() {
|
|
538
|
+
// The create button above the list, as the workflows list has it.
|
|
528
539
|
return html`
|
|
540
|
+
<uui-button class="create" color="default" look="outline" label="Create" @click=${() => this.#new()}></uui-button>
|
|
529
541
|
<uui-box headline="Forms">
|
|
530
|
-
<p>
|
|
531
|
-
A form is a file in <code>schema/forms/</code>. Editing one here writes that
|
|
532
|
-
file, so the change can be reviewed in a pull request like any other type.
|
|
533
|
-
</p>
|
|
534
542
|
${
|
|
535
543
|
this._forms.length === 0
|
|
536
544
|
? html`<p>There are no forms yet.</p>`
|
|
@@ -562,9 +570,6 @@ export default class CromboloFormsOverviewElement extends UmbLitElement {
|
|
|
562
570
|
</uui-table>
|
|
563
571
|
`
|
|
564
572
|
}
|
|
565
|
-
<div class="actions">
|
|
566
|
-
<uui-button look="primary" label="Add a form" @click=${() => this.#new()}></uui-button>
|
|
567
|
-
</div>
|
|
568
573
|
</uui-box>
|
|
569
574
|
`
|
|
570
575
|
}
|
|
@@ -584,18 +589,14 @@ export default class CromboloFormsOverviewElement extends UmbLitElement {
|
|
|
584
589
|
></uui-input>
|
|
585
590
|
</uui-form-layout-item>
|
|
586
591
|
`
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
</ul>
|
|
596
|
-
</div>`
|
|
597
|
-
: nothing
|
|
598
|
-
}
|
|
592
|
+
const leave = () => {
|
|
593
|
+
this._editing = undefined
|
|
594
|
+
this._problems = []
|
|
595
|
+
}
|
|
596
|
+
// Umbraco's own layout, as the workflow designer has it: Save bottom right,
|
|
597
|
+
// and what stopped a save listed just above it.
|
|
598
|
+
return html`<umb-body-layout headline=${form.alias ? `Form: ${form.name || form.alias}` : 'A new form'}>
|
|
599
|
+
<uui-box>
|
|
599
600
|
${text('name', 'Name')}
|
|
600
601
|
${text('alias', 'Alias', 'Letters and numbers, starting with a letter. The file is named after it.')}
|
|
601
602
|
${text('messageOnSubmit', 'Message on submit')}
|
|
@@ -633,14 +634,26 @@ export default class CromboloFormsOverviewElement extends UmbLitElement {
|
|
|
633
634
|
${this._editing.pages.map((page, p) => this.#page(page, p))}
|
|
634
635
|
<div class="actions">
|
|
635
636
|
<uui-button look="secondary" label="Add a page" @click=${() => this.#apply(addPage)}></uui-button>
|
|
636
|
-
<uui-button look="primary" label="Save" ?disabled=${this._busy} @click=${() => this.#save()}></uui-button>
|
|
637
|
-
<uui-button look="secondary" label="Cancel" @click=${() => {
|
|
638
|
-
this._editing = undefined
|
|
639
|
-
this._problems = []
|
|
640
|
-
}}></uui-button>
|
|
641
637
|
</div>
|
|
642
638
|
</uui-box>
|
|
643
|
-
|
|
639
|
+
${
|
|
640
|
+
this._problems.length > 0
|
|
641
|
+
? html`<div class="problems" role="alert">
|
|
642
|
+
<p>This form was not saved:</p>
|
|
643
|
+
<ul>
|
|
644
|
+
${this._problems.map((problem) => html`<li>${problem.path ? html`<code>${problem.path}</code> ` : nothing}${problem.message}</li>`)}
|
|
645
|
+
</ul>
|
|
646
|
+
</div>`
|
|
647
|
+
: nothing
|
|
648
|
+
}
|
|
649
|
+
${
|
|
650
|
+
this._problems.length > 0
|
|
651
|
+
? html`<span slot="footer-info" class="not-saved">Not saved: ${this._problems.length} problem(s), listed above</span>`
|
|
652
|
+
: nothing
|
|
653
|
+
}
|
|
654
|
+
<uui-button slot="actions" look="secondary" label="Back to the list" @click=${leave}>Back to the list</uui-button>
|
|
655
|
+
<uui-button slot="actions" look="primary" color="positive" label="Save" ?disabled=${this._busy} @click=${() => this.#save()}>Save</uui-button>
|
|
656
|
+
</umb-body-layout>`
|
|
644
657
|
}
|
|
645
658
|
|
|
646
659
|
static styles = css`
|
|
@@ -701,8 +714,17 @@ export default class CromboloFormsOverviewElement extends UmbLitElement {
|
|
|
701
714
|
}
|
|
702
715
|
:host {
|
|
703
716
|
display: block;
|
|
717
|
+
height: 100%;
|
|
718
|
+
}
|
|
719
|
+
.list {
|
|
704
720
|
padding: var(--uui-size-layout-1, 24px);
|
|
705
721
|
}
|
|
722
|
+
.create {
|
|
723
|
+
margin-bottom: var(--uui-size-space-5, 18px);
|
|
724
|
+
}
|
|
725
|
+
.not-saved {
|
|
726
|
+
color: var(--uui-color-danger, #d42054);
|
|
727
|
+
}
|
|
706
728
|
p,
|
|
707
729
|
ul {
|
|
708
730
|
max-width: 70ch;
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"$schema": "../../backoffice-dist/dist/umbraco-package-schema.json",
|
|
3
3
|
"name": "Crombolo",
|
|
4
4
|
"id": "Crombolo.Core",
|
|
5
|
-
"version": "0.
|
|
5
|
+
"version": "0.13.2",
|
|
6
6
|
"allowPublicAccess": false,
|
|
7
7
|
"extensions": [
|
|
8
8
|
{
|
|
@@ -246,7 +246,7 @@
|
|
|
246
246
|
"alias": "Crombolo.SectionView.Forms.Overview",
|
|
247
247
|
"name": "Crombolo Forms Overview",
|
|
248
248
|
"element": "/crombolo-plugin/forms-overview.js",
|
|
249
|
-
"weight":
|
|
249
|
+
"weight": 100,
|
|
250
250
|
"meta": {
|
|
251
251
|
"label": "Forms",
|
|
252
252
|
"pathname": "forms",
|
|
@@ -264,7 +264,7 @@
|
|
|
264
264
|
"alias": "Crombolo.SectionView.Forms.Entries",
|
|
265
265
|
"name": "Crombolo Forms Entries",
|
|
266
266
|
"element": "/crombolo-plugin/forms-entries.js",
|
|
267
|
-
"weight":
|
|
267
|
+
"weight": 200,
|
|
268
268
|
"meta": {
|
|
269
269
|
"label": "Entries",
|
|
270
270
|
"pathname": "entries",
|
|
@@ -345,6 +345,112 @@
|
|
|
345
345
|
"name": "Crombolo Icons Context",
|
|
346
346
|
"overwrites": ["Umb.GlobalContext.Icons"],
|
|
347
347
|
"api": "/crombolo-plugin/branding/icon-registry.js"
|
|
348
|
+
},
|
|
349
|
+
{
|
|
350
|
+
"type": "section",
|
|
351
|
+
"alias": "Crombolo.Section.Workflow",
|
|
352
|
+
"name": "Crombolo Workflow Section",
|
|
353
|
+
"weight": 40,
|
|
354
|
+
"meta": {
|
|
355
|
+
"label": "Workflows",
|
|
356
|
+
"pathname": "workflow"
|
|
357
|
+
}
|
|
358
|
+
},
|
|
359
|
+
{
|
|
360
|
+
"type": "sectionView",
|
|
361
|
+
"alias": "Crombolo.SectionView.Workflow.Waiting",
|
|
362
|
+
"name": "Crombolo Workflow Waiting",
|
|
363
|
+
"element": "/crombolo-plugin/workflow-requests.js",
|
|
364
|
+
"elementName": "crombolo-workflow-waiting",
|
|
365
|
+
"weight": 400,
|
|
366
|
+
"meta": {
|
|
367
|
+
"label": "Waiting for me",
|
|
368
|
+
"pathname": "waiting",
|
|
369
|
+
"icon": "icon-inbox"
|
|
370
|
+
},
|
|
371
|
+
"conditions": [
|
|
372
|
+
{
|
|
373
|
+
"alias": "Umb.Condition.SectionAlias",
|
|
374
|
+
"match": "Crombolo.Section.Workflow"
|
|
375
|
+
}
|
|
376
|
+
]
|
|
377
|
+
},
|
|
378
|
+
{
|
|
379
|
+
"type": "sectionView",
|
|
380
|
+
"alias": "Crombolo.SectionView.Workflow.InFlight",
|
|
381
|
+
"name": "Crombolo Workflow In Flight",
|
|
382
|
+
"element": "/crombolo-plugin/workflow-requests.js",
|
|
383
|
+
"elementName": "crombolo-workflow-in-flight",
|
|
384
|
+
"weight": 300,
|
|
385
|
+
"meta": {
|
|
386
|
+
"label": "In flight",
|
|
387
|
+
"pathname": "in-flight",
|
|
388
|
+
"icon": "icon-time"
|
|
389
|
+
},
|
|
390
|
+
"conditions": [
|
|
391
|
+
{
|
|
392
|
+
"alias": "Umb.Condition.SectionAlias",
|
|
393
|
+
"match": "Crombolo.Section.Workflow"
|
|
394
|
+
}
|
|
395
|
+
]
|
|
396
|
+
},
|
|
397
|
+
{
|
|
398
|
+
"type": "sectionView",
|
|
399
|
+
"alias": "Crombolo.SectionView.Workflow.History",
|
|
400
|
+
"name": "Crombolo Workflow History",
|
|
401
|
+
"element": "/crombolo-plugin/workflow-requests.js",
|
|
402
|
+
"elementName": "crombolo-workflow-history",
|
|
403
|
+
"weight": 200,
|
|
404
|
+
"meta": {
|
|
405
|
+
"label": "Decided",
|
|
406
|
+
"pathname": "history",
|
|
407
|
+
"icon": "icon-check"
|
|
408
|
+
},
|
|
409
|
+
"conditions": [
|
|
410
|
+
{
|
|
411
|
+
"alias": "Umb.Condition.SectionAlias",
|
|
412
|
+
"match": "Crombolo.Section.Workflow"
|
|
413
|
+
}
|
|
414
|
+
]
|
|
415
|
+
},
|
|
416
|
+
{
|
|
417
|
+
"type": "sectionView",
|
|
418
|
+
"alias": "Crombolo.SectionView.Workflow.Designer",
|
|
419
|
+
"name": "Crombolo Workflow Designer",
|
|
420
|
+
"element": "/crombolo-plugin/workflow-designer.js",
|
|
421
|
+
"weight": 100,
|
|
422
|
+
"meta": {
|
|
423
|
+
"label": "Workflows",
|
|
424
|
+
"pathname": "workflows",
|
|
425
|
+
"icon": "icon-mindmap"
|
|
426
|
+
},
|
|
427
|
+
"conditions": [
|
|
428
|
+
{
|
|
429
|
+
"alias": "Umb.Condition.SectionAlias",
|
|
430
|
+
"match": "Crombolo.Section.Workflow"
|
|
431
|
+
},
|
|
432
|
+
{
|
|
433
|
+
"alias": "Umb.Condition.CurrentUser.IsAdmin"
|
|
434
|
+
}
|
|
435
|
+
]
|
|
436
|
+
},
|
|
437
|
+
{
|
|
438
|
+
"type": "workspaceView",
|
|
439
|
+
"alias": "Crombolo.WorkspaceView.Document.Workflow",
|
|
440
|
+
"name": "Crombolo Document Workflow",
|
|
441
|
+
"element": "/crombolo-plugin/workflow-tab.js",
|
|
442
|
+
"weight": 50,
|
|
443
|
+
"meta": {
|
|
444
|
+
"label": "Workflow",
|
|
445
|
+
"pathname": "workflow",
|
|
446
|
+
"icon": "icon-mindmap"
|
|
447
|
+
},
|
|
448
|
+
"conditions": [
|
|
449
|
+
{
|
|
450
|
+
"alias": "Umb.Condition.WorkspaceAlias",
|
|
451
|
+
"match": "Umb.Workspace.Document"
|
|
452
|
+
}
|
|
453
|
+
]
|
|
348
454
|
}
|
|
349
455
|
]
|
|
350
456
|
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Workflow screens' calls to <backoffice>/crombolo/api/approvals/ (story 028).
|
|
3
|
+
* The cookie carries the session. Every answer resolves; a refusal carries the
|
|
4
|
+
* server's own words in `message`.
|
|
5
|
+
*/
|
|
6
|
+
const apiBase = () =>
|
|
7
|
+
`${(document.querySelector('base')?.getAttribute('href') ?? '/umbraco/').replace(/\/$/, '')}/crombolo/api/approvals`
|
|
8
|
+
|
|
9
|
+
async function call(path, init = {}) {
|
|
10
|
+
const response = await fetch(`${apiBase()}/${path}`, {
|
|
11
|
+
credentials: 'include',
|
|
12
|
+
...init,
|
|
13
|
+
headers: { accept: 'application/json', 'content-type': 'application/json', ...init.headers },
|
|
14
|
+
})
|
|
15
|
+
const body = await response.json().catch(() => ({}))
|
|
16
|
+
if (response.status === 403 && !body.message)
|
|
17
|
+
return { ok: false, message: 'You are not allowed to do that.' }
|
|
18
|
+
return { ok: response.ok, status: response.status, ...body }
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const post = (path, body = {}) => call(path, { method: 'POST', body: JSON.stringify(body) })
|
|
22
|
+
|
|
23
|
+
export const pageApproval = (key) => call(`page/${encodeURIComponent(key)}`)
|
|
24
|
+
export const sendForApproval = (key, action, comment) =>
|
|
25
|
+
post(`page/${encodeURIComponent(key)}/send`, { action, comment })
|
|
26
|
+
export const decide = (requestKey, verb, body) =>
|
|
27
|
+
post(`request/${encodeURIComponent(requestKey)}/${verb}`, body)
|
|
28
|
+
export const waitingForMe = () => call('waiting')
|
|
29
|
+
export const inFlight = () => call('in-flight')
|
|
30
|
+
export const finished = (skip = 0, take = 25) => call(`history?skip=${skip}&take=${take}`)
|
|
31
|
+
export const listWorkflows = () => call('workflows')
|
|
32
|
+
export const previewWorkflow = (alias, workflow) =>
|
|
33
|
+
post(`workflows/${encodeURIComponent(alias)}/preview`, { workflow })
|
|
34
|
+
export const saveWorkflow = (alias, workflow) =>
|
|
35
|
+
call(`workflows/${encodeURIComponent(alias)}`, {
|
|
36
|
+
method: 'PUT',
|
|
37
|
+
body: JSON.stringify({ workflow }),
|
|
38
|
+
})
|
|
39
|
+
export const deleteWorkflow = (alias) =>
|
|
40
|
+
call(`workflows/${encodeURIComponent(alias)}`, { method: 'DELETE' })
|