Skip to content

Commit 4947212

Browse files
Merge pull request #136 from adobe/sandbox
GH-137 - Sandbox Alert
2 parents 2d54739 + 576627e commit 4947212

File tree

14 files changed

+602
-20
lines changed

14 files changed

+602
-20
lines changed
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
:host {
2+
display: block;
3+
max-width: var(--grid-container-width);
4+
margin: var(--spacing-800) auto var(--spacing-800) auto;
5+
6+
> svg {
7+
display: none;
8+
}
9+
10+
nx-path {
11+
display: block;
12+
margin-bottom: 24px;
13+
}
14+
15+
sl-textarea {
16+
min-height: 600px;
17+
}
18+
}
19+
20+
.nx-detail {
21+
font-weight: 700;
22+
font-size: 14px;
23+
line-height: 1;
24+
margin: 0;
25+
text-transform: uppercase;
26+
}
27+
28+
h1 {
29+
margin-top: 0;
30+
line-height: 1.2;
31+
}
32+
33+
.nx-alert {
34+
/* Info is the default color */
35+
background: linear-gradient(45deg, var(--s2-gray-300) 0%, var(--s2-gray-200));
36+
color: var(-s2-gray-900);
37+
padding: 0 12px;
38+
margin-bottom: 16px;
39+
border-radius: 8px;
40+
height: 36px;
41+
display: flex;
42+
align-items: center;
43+
gap: 12px;
44+
45+
svg {
46+
display: block;
47+
width: 20px;
48+
height: 20px;
49+
}
50+
51+
p {
52+
margin: 0;
53+
}
54+
55+
&.warning {
56+
color: #fff;
57+
background: linear-gradient(45deg, var(--s2-orange-600) 0%, var(--s2-orange-400));
58+
}
59+
60+
&.success {
61+
color: #fff;
62+
background: linear-gradient(45deg, var(--s2-green-900) 0%, var(--s2-green-700));
63+
}
64+
}
65+
66+
.config-preview-table {
67+
display: grid;
68+
gap: 4px;
69+
overflow: hidden;
70+
border-radius: 10px;
71+
margin-bottom: 12px;
72+
73+
.table-row {
74+
display: grid;
75+
gap: 4px;
76+
grid-template-columns: 1fr 1fr 1fr 1fr;
77+
78+
> div {
79+
padding: 4px 8px;
80+
background-color: var(--s2-gray-75);
81+
}
82+
83+
&:first-child {
84+
font-weight: 700;
85+
86+
> div {
87+
padding: 8px;
88+
background-color: var(--s2-gray-100);
89+
}
90+
}
91+
}
92+
}
93+
94+
.config-preview-footer {
95+
display: flex;
96+
justify-content: space-between;
97+
align-items: center;
98+
99+
.da-docs {
100+
color: var(--s2-gray-800);
101+
font-style: italic;
102+
}
103+
104+
.config-preview-action {
105+
display: flex;
106+
align-items: center;
107+
gap: 16px;
108+
}
109+
}

nx/blocks/secure-org/secure-org.js

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
import { html, LitElement, nothing } from 'da-lit';
2+
import { getConfig } from '../../scripts/nexter.js';
3+
import { loadIms } from '../../utils/ims.js';
4+
import getStyle from '../../utils/styles.js';
5+
import getSvg from '../../utils/svg.js';
6+
import { loadConfig, saveConfig } from './utils.js';
7+
8+
import '../../public/sl/components.js';
9+
import '../shared/path/path.js';
10+
11+
const { nxBase: nx } = getConfig();
12+
13+
const ICONS = [
14+
`${nx}/public/icons/S2_Icon_InfoCircle_20_N.svg`,
15+
`${nx}/public/icons/S2_Icon_AlertDiamond_20_N.svg`,
16+
`${nx}/public/icons/S2_Icon_CheckmarkCircle_20_N.svg`,
17+
];
18+
19+
const EL_NAME = 'nx-secure-org';
20+
21+
const styles = await getStyle(import.meta.url);
22+
const icons = await getSvg({ paths: ICONS });
23+
24+
class SecureOrg extends LitElement {
25+
static properties = {
26+
_org: { state: true },
27+
_alert: { state: true },
28+
_user: { state: true },
29+
_actionText: { state: true },
30+
_saving: { state: true },
31+
_authorized: { state: true },
32+
_existingConfig: { state: true },
33+
};
34+
35+
connectedCallback() {
36+
super.connectedCallback();
37+
this.shadowRoot.adoptedStyleSheets = [styles];
38+
this.shadowRoot.append(...icons);
39+
this.resetDefaults();
40+
}
41+
42+
resetDefaults() {
43+
this._user = undefined;
44+
this._alert = undefined;
45+
this._saving = undefined;
46+
this._actionText = 'Update config';
47+
}
48+
49+
async handleDetail({ detail }) {
50+
this.resetDefaults();
51+
this._org = detail.org;
52+
53+
if (!this._org) {
54+
this._alert = {
55+
type: 'warning',
56+
message: 'Please enter an org path to check sandbox status.',
57+
};
58+
return;
59+
}
60+
61+
const user = await loadIms();
62+
if (user.emailVerified !== 'true') {
63+
this._alert = {
64+
type: 'warning',
65+
message: 'Email has not been verified.',
66+
};
67+
return;
68+
}
69+
70+
const { message, json } = await loadConfig(this._org);
71+
if (message) {
72+
this._alert = { type: 'warning', message };
73+
return;
74+
}
75+
76+
if (json?.permissions) {
77+
this._alert = { type: 'success', message: 'This org has permissions set.' };
78+
return;
79+
}
80+
81+
if (json) {
82+
this._existingConfig = json;
83+
}
84+
85+
// Set the user if email is verified.
86+
this._user = user;
87+
}
88+
89+
async handleUpdateConfig() {
90+
this._saving = true;
91+
this._actionText = 'Updating';
92+
await saveConfig(this._org, this._user.email, this._existingConfig);
93+
}
94+
95+
handleCheck() {
96+
this._authorized = !this._authorized;
97+
}
98+
99+
renderAlert() {
100+
if (!this._alert) return nothing;
101+
102+
const type2icon = {
103+
info: 'InfoCircle',
104+
warning: 'AlertDiamond',
105+
success: 'CheckmarkCircle',
106+
};
107+
108+
return html`
109+
<div class="nx-alert ${this._alert.type || 'info'}">
110+
<svg class="icon"><use href="#S2_Icon_${type2icon[this._alert.type || 'info']}_20_N"/></svg>
111+
<p>${this._alert.message}</p>
112+
</div>
113+
`;
114+
}
115+
116+
renderPreview() {
117+
if (!this._user) return nothing;
118+
119+
return html`
120+
<p class="nx-detail">Update sandbox</p>
121+
<h1>${this._org}</h1>
122+
<div class="config-preview">
123+
<div class="config-preview-table">
124+
<div class="table-row">
125+
<div>Path</div>
126+
<div>Groups</div>
127+
<div>Actions</div>
128+
<div>Comments</div>
129+
</div>
130+
<div class="table-row">
131+
<div>CONFIG</div>
132+
<div>${this._user.email}</div>
133+
<div>write</div>
134+
<div>The ability to set configurations for an org.</div>
135+
</div>
136+
<div class="table-row">
137+
<div>/ + **</div>
138+
<div>${this._user.email}</div>
139+
<div>write</div>
140+
<div>The ability to create content.</div>
141+
</div>
142+
</div>
143+
<div class="config-preview-footer">
144+
<a class="da-docs" href="https://docs.da.live/administrators/guides/permissions" target="_blank">
145+
Read permission documentation
146+
</a>
147+
<div class="config-preview-action">
148+
<div>
149+
<input type="checkbox" id="authorize" name="authorize" @change=${this.handleCheck} ?checked=${this._authorized} />
150+
<label for="authorize">I am legally authorized to make decisions for this organization.</label>
151+
</div>
152+
<sl-button @click=${this.handleUpdateConfig} ?disabled=${this._saving || !this._authorized}>${this._actionText}</sl-button>
153+
</div>
154+
</div>
155+
</div>
156+
`;
157+
}
158+
159+
render() {
160+
return html`
161+
<nx-path label="Load organization" @details=${this.handleDetail}></nx-path>
162+
${this.renderAlert()}
163+
${this.renderPreview()}
164+
`;
165+
}
166+
}
167+
168+
customElements.define(EL_NAME, SecureOrg);
169+
170+
export default function init(el) {
171+
el.replaceChildren();
172+
let cmp = el.querySelector(EL_NAME);
173+
if (!cmp) {
174+
cmp = document.createElement(EL_NAME);
175+
el.append(cmp);
176+
}
177+
}

nx/blocks/secure-org/utils.js

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import { DA_ORIGIN } from '../../public/utils/constants.js';
2+
import { daFetch } from '../../utils/daFetch.js';
3+
4+
const DEF_CONFIG = `{
5+
"data": {
6+
"total": 1,
7+
"limit": 1,
8+
"offset": 0,
9+
"data": [{}]
10+
},
11+
"permissions": {
12+
"total": 2,
13+
"limit": 2,
14+
"offset": 0,
15+
"data": [
16+
{
17+
"path": "CONFIG",
18+
"groups": "{{EMAIL}}",
19+
"actions": "write",
20+
"comments": "The ability to set configurations for an org."
21+
},
22+
{
23+
"path": "/ + **",
24+
"groups": "{{EMAIL}}",
25+
"actions": "write",
26+
"comments": "The ability to create content."
27+
}
28+
]
29+
},
30+
":names": [
31+
"data",
32+
"permissions"
33+
],
34+
":version": 3,
35+
":type": "multi-sheet"
36+
}`;
37+
38+
async function fetchConfig(org, body) {
39+
let opts;
40+
if (body) opts = { method: 'POST', body };
41+
42+
return daFetch(`${DA_ORIGIN}/config/${org}/`, opts);
43+
}
44+
45+
export async function loadConfig(org) {
46+
const resp = await fetchConfig(org);
47+
48+
const result = { status: resp.status };
49+
50+
if (!resp.ok) {
51+
if (resp.status === 403 && resp.status === 401) {
52+
result.message = 'You are not authorized to change this organization.';
53+
}
54+
} else {
55+
const json = await resp.json();
56+
if (json) result.json = json;
57+
}
58+
59+
return result;
60+
}
61+
62+
export async function saveConfig(org, email, existingConfig) {
63+
const defConfigStr = DEF_CONFIG.replaceAll('{{EMAIL}}', email);
64+
const defConfig = JSON.parse(defConfigStr);
65+
66+
if (existingConfig) {
67+
defConfig.data = existingConfig.default
68+
|| existingConfig.data
69+
|| existingConfig;
70+
}
71+
72+
const body = new FormData();
73+
body.append('config', JSON.stringify(defConfig));
74+
75+
await fetchConfig(org, body);
76+
77+
window.location.reload(true);
78+
}

nx/blocks/shared/path/path.css

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
form {
2+
display: flex;
3+
gap: 12px;
4+
5+
sl-input {
6+
flex: 1 0 auto;
7+
}
8+
}

0 commit comments

Comments
 (0)