@growthub/cli 0.9.4 → 0.9.6
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/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/route.js +64 -4
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/globals.css +223 -7
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/page.jsx +11 -105
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/workspace-builder.jsx +951 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-config.js +112 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-schema.js +461 -0
- package/dist/index.js +1825 -1061
- package/package.json +3 -2
|
@@ -6,6 +6,14 @@ import { describePaymentAdapter } from "@/lib/adapters/payments";
|
|
|
6
6
|
import { describePersistenceAdapter } from "@/lib/adapters/persistence";
|
|
7
7
|
import { groupIntegrationsByLane } from "@/lib/domain/integrations";
|
|
8
8
|
import { buildPortalWorkspace, portalCapabilities } from "@/lib/domain/portal";
|
|
9
|
+
import {
|
|
10
|
+
describePersistenceMode,
|
|
11
|
+
readWorkspaceConfig,
|
|
12
|
+
writeWorkspaceConfig
|
|
13
|
+
} from "@/lib/workspace-config";
|
|
14
|
+
|
|
15
|
+
const ALLOWED_PATCH_FIELDS = new Set(["dashboards", "widgetTypes", "canvas"]);
|
|
16
|
+
|
|
9
17
|
async function GET() {
|
|
10
18
|
const integrations = await listAgencyPortalIntegrations();
|
|
11
19
|
const config = readAdapterConfig();
|
|
@@ -18,14 +26,66 @@ async function GET() {
|
|
|
18
26
|
const settings = {
|
|
19
27
|
integrations: groupIntegrationsByLane(integrations)
|
|
20
28
|
};
|
|
29
|
+
const workspaceConfig = await readWorkspaceConfig();
|
|
30
|
+
const persistence = describePersistenceMode();
|
|
21
31
|
return NextResponse.json({
|
|
22
32
|
config,
|
|
23
33
|
adapters,
|
|
24
34
|
capabilities: portalCapabilities,
|
|
25
35
|
settings,
|
|
26
|
-
workspace: buildPortalWorkspace({ config, adapters, integrations: settings.integrations })
|
|
36
|
+
workspace: buildPortalWorkspace({ config, adapters, integrations: settings.integrations }),
|
|
37
|
+
workspaceConfig,
|
|
38
|
+
workspaceConfigPersistence: persistence
|
|
27
39
|
});
|
|
28
40
|
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
41
|
+
|
|
42
|
+
async function PATCH(request) {
|
|
43
|
+
let patch;
|
|
44
|
+
try {
|
|
45
|
+
patch = await request.json();
|
|
46
|
+
} catch {
|
|
47
|
+
return NextResponse.json({ error: "invalid json body" }, { status: 400 });
|
|
48
|
+
}
|
|
49
|
+
if (!patch || typeof patch !== "object" || Array.isArray(patch)) {
|
|
50
|
+
return NextResponse.json({ error: "patch must be a plain object" }, { status: 400 });
|
|
51
|
+
}
|
|
52
|
+
const unknown = Object.keys(patch).filter((key) => !ALLOWED_PATCH_FIELDS.has(key));
|
|
53
|
+
if (unknown.length) {
|
|
54
|
+
return NextResponse.json(
|
|
55
|
+
{ error: "patch contains unknown fields", details: unknown, allowed: Array.from(ALLOWED_PATCH_FIELDS) },
|
|
56
|
+
{ status: 400 }
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
const sanitized = {};
|
|
60
|
+
for (const key of ALLOWED_PATCH_FIELDS) {
|
|
61
|
+
if (Object.prototype.hasOwnProperty.call(patch, key)) {
|
|
62
|
+
sanitized[key] = patch[key];
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
try {
|
|
66
|
+
const next = await writeWorkspaceConfig(sanitized);
|
|
67
|
+
return NextResponse.json({ workspaceConfig: next });
|
|
68
|
+
} catch (error) {
|
|
69
|
+
if (error.code === "WORKSPACE_PERSISTENCE_READ_ONLY") {
|
|
70
|
+
return NextResponse.json(
|
|
71
|
+
{
|
|
72
|
+
error: "workspace config is read-only in this runtime",
|
|
73
|
+
reason: error.message,
|
|
74
|
+
adapter: error.adapter,
|
|
75
|
+
guidance:
|
|
76
|
+
"Edit growthub.config.json locally, or set WORKSPACE_CONFIG_ALLOW_FS_WRITE=true on a writable runtime."
|
|
77
|
+
},
|
|
78
|
+
{ status: 409 }
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
if (error.code === "WORKSPACE_PERSISTENCE_PATH_REFUSED") {
|
|
82
|
+
return NextResponse.json({ error: error.message }, { status: 500 });
|
|
83
|
+
}
|
|
84
|
+
if (error.code === "INVALID_WORKSPACE_CONFIG") {
|
|
85
|
+
return NextResponse.json({ error: error.message, details: error.details }, { status: 400 });
|
|
86
|
+
}
|
|
87
|
+
return NextResponse.json({ error: error.message || "failed to write workspace config" }, { status: 500 });
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export { GET, PATCH };
|
package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/globals.css
CHANGED
|
@@ -544,23 +544,46 @@ code { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 0
|
|
|
544
544
|
background: #f3f3f3;
|
|
545
545
|
color: #222;
|
|
546
546
|
}
|
|
547
|
+
.workspace-toolbar-actions select,
|
|
548
|
+
.workspace-widget-settings select {
|
|
549
|
+
min-height: 32px;
|
|
550
|
+
border: 1px solid #dcdcdc;
|
|
551
|
+
border-radius: 6px;
|
|
552
|
+
background: #ffffff;
|
|
553
|
+
color: #444;
|
|
554
|
+
font: inherit;
|
|
555
|
+
padding: 0 8px;
|
|
556
|
+
}
|
|
557
|
+
.workspace-hidden-input {
|
|
558
|
+
display: none;
|
|
559
|
+
}
|
|
560
|
+
.workspace-config-message {
|
|
561
|
+
margin: -8px 0 10px;
|
|
562
|
+
color: #777;
|
|
563
|
+
font-size: 12px;
|
|
564
|
+
}
|
|
547
565
|
.workspace-grid {
|
|
548
566
|
position: relative;
|
|
549
567
|
display: grid;
|
|
550
568
|
grid-template-columns: repeat(var(--workspace-columns), minmax(48px, 1fr));
|
|
569
|
+
grid-template-rows: repeat(var(--workspace-rows), 52px);
|
|
551
570
|
grid-auto-rows: 52px;
|
|
552
571
|
gap: 8px;
|
|
553
|
-
min-height: 620px;
|
|
554
572
|
padding: 8px;
|
|
555
573
|
}
|
|
556
574
|
.workspace-grid-cell {
|
|
575
|
+
appearance: none;
|
|
557
576
|
border: 1px solid #ededed;
|
|
558
577
|
border-radius: 7px;
|
|
559
578
|
background: #fdfdfd;
|
|
579
|
+
cursor: crosshair;
|
|
580
|
+
padding: 0;
|
|
581
|
+
}
|
|
582
|
+
.workspace-grid-cell:disabled {
|
|
583
|
+
pointer-events: none;
|
|
584
|
+
opacity: 1;
|
|
560
585
|
}
|
|
561
586
|
.workspace-add-widget {
|
|
562
|
-
grid-column: 1 / span 4;
|
|
563
|
-
grid-row: 1 / span 4;
|
|
564
587
|
z-index: 1;
|
|
565
588
|
display: grid;
|
|
566
589
|
place-items: center;
|
|
@@ -572,6 +595,11 @@ code { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 0
|
|
|
572
595
|
color: #3d3d3d;
|
|
573
596
|
cursor: pointer;
|
|
574
597
|
}
|
|
598
|
+
.workspace-add-widget.selecting {
|
|
599
|
+
border-color: #7aa2ff;
|
|
600
|
+
background: rgba(122, 162, 255, 0.16);
|
|
601
|
+
box-shadow: inset 0 0 0 1px rgba(84, 125, 255, 0.35);
|
|
602
|
+
}
|
|
575
603
|
.workspace-add-widget small {
|
|
576
604
|
color: #979797;
|
|
577
605
|
}
|
|
@@ -593,16 +621,138 @@ code { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 0
|
|
|
593
621
|
background: #b9ecff;
|
|
594
622
|
}
|
|
595
623
|
.workspace-widget-preview {
|
|
624
|
+
position: relative;
|
|
596
625
|
z-index: 2;
|
|
597
626
|
border: 1px solid #dedede;
|
|
598
627
|
border-radius: 7px;
|
|
599
628
|
background: #ffffff;
|
|
600
|
-
padding:
|
|
629
|
+
padding: 12px;
|
|
630
|
+
cursor: pointer;
|
|
631
|
+
overflow: hidden;
|
|
601
632
|
}
|
|
602
|
-
.workspace-widget-preview
|
|
633
|
+
.workspace-widget-preview.selected {
|
|
634
|
+
border-color: #3f68ff;
|
|
635
|
+
box-shadow: inset 0 0 0 1px rgba(63, 104, 255, 0.45);
|
|
636
|
+
}
|
|
637
|
+
.workspace-resize-handle {
|
|
638
|
+
position: absolute;
|
|
639
|
+
z-index: 3;
|
|
640
|
+
width: 13px;
|
|
641
|
+
height: 13px;
|
|
642
|
+
border: 1px solid #3f68ff;
|
|
643
|
+
border-radius: 4px;
|
|
644
|
+
background: #ffffff;
|
|
645
|
+
box-shadow: 0 1px 3px rgba(63, 104, 255, 0.22);
|
|
646
|
+
padding: 0;
|
|
647
|
+
}
|
|
648
|
+
.workspace-resize-handle.nw {
|
|
649
|
+
top: -6px;
|
|
650
|
+
left: -6px;
|
|
651
|
+
cursor: nwse-resize;
|
|
652
|
+
}
|
|
653
|
+
.workspace-resize-handle.ne {
|
|
654
|
+
top: -6px;
|
|
655
|
+
right: -6px;
|
|
656
|
+
cursor: nesw-resize;
|
|
657
|
+
}
|
|
658
|
+
.workspace-resize-handle.sw {
|
|
659
|
+
bottom: -6px;
|
|
660
|
+
left: -6px;
|
|
661
|
+
cursor: nesw-resize;
|
|
662
|
+
}
|
|
663
|
+
.workspace-resize-handle.se {
|
|
664
|
+
right: -6px;
|
|
665
|
+
bottom: -6px;
|
|
666
|
+
cursor: nwse-resize;
|
|
667
|
+
}
|
|
668
|
+
.workspace-widget-preview-title {
|
|
669
|
+
display: flex;
|
|
670
|
+
align-items: center;
|
|
671
|
+
gap: 8px;
|
|
672
|
+
min-height: 22px;
|
|
673
|
+
}
|
|
674
|
+
.workspace-widget-preview-title span,
|
|
675
|
+
.workspace-widget-preview-title button {
|
|
603
676
|
color: #888;
|
|
604
677
|
font-size: 11px;
|
|
605
678
|
}
|
|
679
|
+
.workspace-widget-preview-title strong {
|
|
680
|
+
min-width: 0;
|
|
681
|
+
flex: 1;
|
|
682
|
+
overflow: hidden;
|
|
683
|
+
color: #333;
|
|
684
|
+
font-size: 13px;
|
|
685
|
+
text-overflow: ellipsis;
|
|
686
|
+
white-space: nowrap;
|
|
687
|
+
}
|
|
688
|
+
.workspace-widget-preview-title button {
|
|
689
|
+
width: 22px;
|
|
690
|
+
height: 22px;
|
|
691
|
+
border: 0;
|
|
692
|
+
border-radius: 5px;
|
|
693
|
+
background: transparent;
|
|
694
|
+
}
|
|
695
|
+
.workspace-widget-preview:not(.selected) .workspace-widget-preview-title button {
|
|
696
|
+
visibility: hidden;
|
|
697
|
+
}
|
|
698
|
+
.workspace-view-table {
|
|
699
|
+
margin-top: 10px;
|
|
700
|
+
border: 1px solid #eeeeee;
|
|
701
|
+
border-radius: 4px;
|
|
702
|
+
overflow: hidden;
|
|
703
|
+
color: #555;
|
|
704
|
+
font-size: 12px;
|
|
705
|
+
}
|
|
706
|
+
.workspace-view-table div {
|
|
707
|
+
display: grid;
|
|
708
|
+
grid-template-columns: repeat(var(--workspace-view-columns, 2), minmax(0, 1fr));
|
|
709
|
+
min-height: 25px;
|
|
710
|
+
border-bottom: 1px solid #eeeeee;
|
|
711
|
+
}
|
|
712
|
+
.workspace-view-table span {
|
|
713
|
+
min-width: 0;
|
|
714
|
+
overflow: hidden;
|
|
715
|
+
padding: 5px 8px;
|
|
716
|
+
text-overflow: ellipsis;
|
|
717
|
+
white-space: nowrap;
|
|
718
|
+
}
|
|
719
|
+
.workspace-view-table span + span {
|
|
720
|
+
border-left: 1px solid #eeeeee;
|
|
721
|
+
}
|
|
722
|
+
.workspace-view-table footer {
|
|
723
|
+
padding: 7px 8px;
|
|
724
|
+
color: #999;
|
|
725
|
+
}
|
|
726
|
+
.workspace-iframe-preview {
|
|
727
|
+
height: calc(100% - 34px);
|
|
728
|
+
min-height: 90px;
|
|
729
|
+
display: grid;
|
|
730
|
+
place-items: center;
|
|
731
|
+
margin-top: 10px;
|
|
732
|
+
border-radius: 7px;
|
|
733
|
+
background: #fdfdfd;
|
|
734
|
+
color: #d54040;
|
|
735
|
+
font-size: 12px;
|
|
736
|
+
}
|
|
737
|
+
.workspace-rich-text-preview {
|
|
738
|
+
margin: 18px 10px 0;
|
|
739
|
+
color: #444;
|
|
740
|
+
font-size: 13px;
|
|
741
|
+
line-height: 1.5;
|
|
742
|
+
}
|
|
743
|
+
.workspace-chart-preview {
|
|
744
|
+
height: calc(100% - 42px);
|
|
745
|
+
min-height: 96px;
|
|
746
|
+
display: flex;
|
|
747
|
+
align-items: end;
|
|
748
|
+
gap: 8px;
|
|
749
|
+
padding: 16px 10px 6px;
|
|
750
|
+
}
|
|
751
|
+
.workspace-chart-preview span {
|
|
752
|
+
flex: 1;
|
|
753
|
+
border-radius: 5px 5px 0 0;
|
|
754
|
+
background: #d9e4ff;
|
|
755
|
+
}
|
|
606
756
|
.workspace-widget-panel {
|
|
607
757
|
border-left: 1px solid #e8e8e8;
|
|
608
758
|
padding: 0 12px 16px;
|
|
@@ -614,16 +764,28 @@ code { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 0
|
|
|
614
764
|
gap: 10px;
|
|
615
765
|
border-bottom: 1px solid #ececec;
|
|
616
766
|
}
|
|
617
|
-
.workspace-panel-title button
|
|
767
|
+
.workspace-panel-title button,
|
|
768
|
+
.workspace-panel-title > span {
|
|
618
769
|
width: 24px;
|
|
619
770
|
height: 24px;
|
|
771
|
+
display: grid;
|
|
772
|
+
place-items: center;
|
|
620
773
|
border: 0;
|
|
621
774
|
border-radius: 5px;
|
|
622
775
|
background: #f1f1f1;
|
|
623
776
|
color: #888;
|
|
624
777
|
}
|
|
625
778
|
.workspace-panel-title strong {
|
|
779
|
+
min-width: 0;
|
|
626
780
|
font-size: 14px;
|
|
781
|
+
overflow: hidden;
|
|
782
|
+
text-overflow: ellipsis;
|
|
783
|
+
white-space: nowrap;
|
|
784
|
+
}
|
|
785
|
+
.workspace-panel-title em {
|
|
786
|
+
color: #999;
|
|
787
|
+
font-size: 12px;
|
|
788
|
+
font-style: normal;
|
|
627
789
|
}
|
|
628
790
|
.workspace-panel-label {
|
|
629
791
|
margin: 14px 0 7px;
|
|
@@ -647,7 +809,6 @@ code { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 0
|
|
|
647
809
|
padding: 0 8px;
|
|
648
810
|
text-align: left;
|
|
649
811
|
}
|
|
650
|
-
.workspace-widget-types button:first-child,
|
|
651
812
|
.workspace-widget-types button:hover {
|
|
652
813
|
background: #f2f2f2;
|
|
653
814
|
}
|
|
@@ -678,6 +839,61 @@ code { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 0
|
|
|
678
839
|
color: #777;
|
|
679
840
|
background: #f4f4f4;
|
|
680
841
|
}
|
|
842
|
+
.workspace-widget-settings {
|
|
843
|
+
display: grid;
|
|
844
|
+
gap: 14px;
|
|
845
|
+
padding-top: 14px;
|
|
846
|
+
}
|
|
847
|
+
.workspace-field-stack {
|
|
848
|
+
display: grid;
|
|
849
|
+
gap: 12px;
|
|
850
|
+
}
|
|
851
|
+
.workspace-widget-settings label {
|
|
852
|
+
display: grid;
|
|
853
|
+
gap: 6px;
|
|
854
|
+
}
|
|
855
|
+
.workspace-widget-settings label span {
|
|
856
|
+
color: #999;
|
|
857
|
+
font-size: 11px;
|
|
858
|
+
font-weight: 700;
|
|
859
|
+
}
|
|
860
|
+
.workspace-widget-settings input,
|
|
861
|
+
.workspace-widget-settings textarea {
|
|
862
|
+
width: 100%;
|
|
863
|
+
box-sizing: border-box;
|
|
864
|
+
border: 1px solid #e5e5e5;
|
|
865
|
+
border-radius: 6px;
|
|
866
|
+
background: #fafafa;
|
|
867
|
+
color: #444;
|
|
868
|
+
font: inherit;
|
|
869
|
+
padding: 8px 9px;
|
|
870
|
+
}
|
|
871
|
+
.workspace-widget-settings textarea {
|
|
872
|
+
min-height: 120px;
|
|
873
|
+
resize: vertical;
|
|
874
|
+
}
|
|
875
|
+
.workspace-settings-list {
|
|
876
|
+
display: grid;
|
|
877
|
+
gap: 2px;
|
|
878
|
+
}
|
|
879
|
+
.workspace-settings-list div {
|
|
880
|
+
display: flex;
|
|
881
|
+
align-items: center;
|
|
882
|
+
justify-content: space-between;
|
|
883
|
+
min-height: 32px;
|
|
884
|
+
border-radius: 5px;
|
|
885
|
+
color: #666;
|
|
886
|
+
font-size: 13px;
|
|
887
|
+
padding: 0 8px;
|
|
888
|
+
}
|
|
889
|
+
.workspace-settings-list div:nth-child(3),
|
|
890
|
+
.workspace-settings-list div:hover {
|
|
891
|
+
background: #f3f3f3;
|
|
892
|
+
}
|
|
893
|
+
.workspace-settings-list code {
|
|
894
|
+
color: #aaa;
|
|
895
|
+
background: transparent;
|
|
896
|
+
}
|
|
681
897
|
|
|
682
898
|
@media (max-width: 1080px) {
|
|
683
899
|
.workspace-builder {
|
|
@@ -1,115 +1,21 @@
|
|
|
1
1
|
import workspaceConfig from "../growthub.config.json";
|
|
2
2
|
import { readAdapterConfig } from "@/lib/adapters/env";
|
|
3
3
|
import { describeIntegrationAdapter } from "@/lib/adapters/integrations";
|
|
4
|
-
import
|
|
4
|
+
import { describePersistenceMode } from "@/lib/workspace-config";
|
|
5
|
+
import WorkspaceBuilder from "./workspace-builder.jsx";
|
|
5
6
|
|
|
6
7
|
function Home() {
|
|
7
8
|
const adapterConfig = readAdapterConfig();
|
|
8
9
|
const integrationAdapter = describeIntegrationAdapter();
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
</div>
|
|
19
|
-
<nav className="workspace-nav">
|
|
20
|
-
<a className="active" href="#dashboards">Dashboards</a>
|
|
21
|
-
<a href="#canvas">Canvas</a>
|
|
22
|
-
<a href="#widgets">Widgets</a>
|
|
23
|
-
<a href="#bindings">Bindings</a>
|
|
24
|
-
<Link href="/settings/integrations">Integrations</Link>
|
|
25
|
-
</nav>
|
|
26
|
-
<div className="workspace-rail-status">
|
|
27
|
-
<span className="status-dot" />
|
|
28
|
-
{integrationAdapter.authority}
|
|
29
|
-
</div>
|
|
30
|
-
</aside>
|
|
31
|
-
|
|
32
|
-
<section className="workspace-surface">
|
|
33
|
-
<header className="workspace-toolbar">
|
|
34
|
-
<div>
|
|
35
|
-
<p>Official starter</p>
|
|
36
|
-
<h1>{workspaceConfig.name}</h1>
|
|
37
|
-
</div>
|
|
38
|
-
<div className="workspace-toolbar-actions">
|
|
39
|
-
<button type="button">New Dashboard</button>
|
|
40
|
-
<button type="button">Save</button>
|
|
41
|
-
</div>
|
|
42
|
-
</header>
|
|
43
|
-
|
|
44
|
-
<section className="workspace-table" id="dashboards" aria-label="Dashboards">
|
|
45
|
-
<div className="workspace-table-heading">
|
|
46
|
-
<strong>Dashboards</strong>
|
|
47
|
-
<span>{dashboards.length} template</span>
|
|
48
|
-
</div>
|
|
49
|
-
<div className="workspace-table-row workspace-table-head">
|
|
50
|
-
<span>Title</span>
|
|
51
|
-
<span>Created by</span>
|
|
52
|
-
<span>Last update</span>
|
|
53
|
-
<span>Status</span>
|
|
54
|
-
</div>
|
|
55
|
-
{dashboards.map((dashboard) => <div className="workspace-table-row" key={dashboard.id}>
|
|
56
|
-
<span>{dashboard.name}</span>
|
|
57
|
-
<span>{dashboard.createdBy}</span>
|
|
58
|
-
<span>{dashboard.updatedAt}</span>
|
|
59
|
-
<code>{dashboard.status}</code>
|
|
60
|
-
</div>)}
|
|
61
|
-
</section>
|
|
62
|
-
|
|
63
|
-
<section className="workspace-canvas" id="canvas" aria-label="Composable dashboard canvas">
|
|
64
|
-
<div className="workspace-tabs">
|
|
65
|
-
<button className="active" type="button">{canvas.name}</button>
|
|
66
|
-
<button type="button">New Tab</button>
|
|
67
|
-
</div>
|
|
68
|
-
<div className="workspace-grid" style={{ "--workspace-columns": canvas.layout.columns }}>
|
|
69
|
-
<button className="workspace-add-widget" type="button">
|
|
70
|
-
<span className="workspace-widget-icon" aria-hidden="true"><span /></span>
|
|
71
|
-
<strong>Add widget</strong>
|
|
72
|
-
<small>Click to add your first widget</small>
|
|
73
|
-
</button>
|
|
74
|
-
{Array.from({ length: 96 }).map((_, index) => <span aria-hidden="true" className="workspace-grid-cell" key={index} />)}
|
|
75
|
-
{canvas.widgets.map((widget) => <article className="workspace-widget-preview" key={widget.id} style={{
|
|
76
|
-
gridColumn: `${widget.position.x + 1} / span ${widget.position.w}`,
|
|
77
|
-
gridRow: `${widget.position.y + 1} / span ${widget.position.h}`
|
|
78
|
-
}}>
|
|
79
|
-
<span>{widget.kind}</span>
|
|
80
|
-
<strong>{widget.title}</strong>
|
|
81
|
-
</article>)}
|
|
82
|
-
</div>
|
|
83
|
-
</section>
|
|
84
|
-
</section>
|
|
85
|
-
|
|
86
|
-
<aside className="workspace-widget-panel" id="widgets" aria-label="Widget configuration">
|
|
87
|
-
<div className="workspace-panel-title">
|
|
88
|
-
<button type="button" aria-label="Close widget panel">x</button>
|
|
89
|
-
<strong>New widget</strong>
|
|
90
|
-
</div>
|
|
91
|
-
<section>
|
|
92
|
-
<p className="workspace-panel-label">Widget type</p>
|
|
93
|
-
<div className="workspace-widget-types">
|
|
94
|
-
{widgetTypes.map((widget) => <button type="button" key={widget.kind}>
|
|
95
|
-
<span>{widget.icon}</span>
|
|
96
|
-
{widget.label}
|
|
97
|
-
</button>)}
|
|
98
|
-
</div>
|
|
99
|
-
</section>
|
|
100
|
-
<section className="workspace-bindings" id="bindings">
|
|
101
|
-
<p className="workspace-panel-label">Config bindings</p>
|
|
102
|
-
{Object.entries(canvas.bindings).map(([key, value]) => <div key={key}>
|
|
103
|
-
<span>{key}</span>
|
|
104
|
-
<code>{String(value)}</code>
|
|
105
|
-
</div>)}
|
|
106
|
-
<div>
|
|
107
|
-
<span>integrationAdapter</span>
|
|
108
|
-
<code>{adapterConfig.integrationAdapter}</code>
|
|
109
|
-
</div>
|
|
110
|
-
</section>
|
|
111
|
-
</aside>
|
|
112
|
-
</main>;
|
|
10
|
+
const persistence = describePersistenceMode();
|
|
11
|
+
return (
|
|
12
|
+
<WorkspaceBuilder
|
|
13
|
+
initialConfig={workspaceConfig}
|
|
14
|
+
adapterConfig={adapterConfig}
|
|
15
|
+
integrationAdapter={integrationAdapter}
|
|
16
|
+
persistence={persistence}
|
|
17
|
+
/>
|
|
18
|
+
);
|
|
113
19
|
}
|
|
114
20
|
|
|
115
21
|
export {
|