-
+
diff --git a/apps/ui/html/workspace-setup.html b/apps/ui/html/workspace-setup.html
index 6aab8a5..14ca06d 100644
--- a/apps/ui/html/workspace-setup.html
+++ b/apps/ui/html/workspace-setup.html
@@ -14,6 +14,7 @@
+
@@ -304,14 +305,14 @@
Export all data
Download a ZIP archive of all operation configs, schemas, mappings and invocation history as JSON.
-
+
Delete workspace
Permanently deletes all operations, keys, logs and settings. This action cannot be undone. You will be logged out immediately.
-
+
diff --git a/apps/ui/js/api.js b/apps/ui/js/api.js
index e5f3d58..f8dd2be 100644
--- a/apps/ui/js/api.js
+++ b/apps/ui/js/api.js
@@ -92,6 +92,27 @@
listWorkspaces: function() {
return get('/workspaces');
},
+ createWorkspace: function(payload) {
+ return post('/workspaces', payload);
+ },
+ getWorkspace: function(workspaceId) {
+ return get('/workspaces/' + encodeURIComponent(workspaceId));
+ },
+ updateWorkspace: function(workspaceId, payload) {
+ return patch('/workspaces/' + encodeURIComponent(workspaceId), payload);
+ },
+ listMemberships: function(workspaceId) {
+ return get('/workspaces/' + encodeURIComponent(workspaceId) + '/members');
+ },
+ listInvitations: function(workspaceId) {
+ return get('/workspaces/' + encodeURIComponent(workspaceId) + '/invitations');
+ },
+ createInvitation: function(workspaceId, payload) {
+ return post('/workspaces/' + encodeURIComponent(workspaceId) + '/invitations', payload);
+ },
+ deleteInvitation: function(workspaceId, invitationId) {
+ return del('/workspaces/' + encodeURIComponent(workspaceId) + '/invitations/' + encodeURIComponent(invitationId));
+ },
listOperations: function(workspaceId) {
return get('/workspaces/' + encodeURIComponent(workspaceId) + '/operations');
},
diff --git a/apps/ui/js/settings.js b/apps/ui/js/settings.js
index 885c46a..d97418b 100644
--- a/apps/ui/js/settings.js
+++ b/apps/ui/js/settings.js
@@ -1,25 +1,39 @@
-// Populate profile from stored user
-try {
- var u = JSON.parse(localStorage.getItem('crank_user'));
- if (u) {
- document.getElementById('profile-avatar').textContent = u.initials || 'AT';
- document.getElementById('profile-display-name').textContent = u.name || 'Operator';
- document.getElementById('profile-email').textContent = u.email || '';
- var fi = document.getElementById('field-firstname');
- var fe = document.getElementById('field-email');
- if (fi && u.name) fi.value = u.name;
- if (fe && u.email) fe.value = u.email;
- }
-} catch (e) {}
+function populateProfile() {
+ try {
+ var user = JSON.parse(localStorage.getItem('crank_user'));
+ if (!user) {
+ return;
+ }
-// Inject language switcher into section-profile card body, before Save button
-(function() {
+ document.getElementById('profile-avatar').textContent = user.initials || 'AT';
+ document.getElementById('profile-display-name').textContent = user.name || 'Operator';
+ document.getElementById('profile-email').textContent = user.email || '';
+
+ var firstName = document.getElementById('field-firstname');
+ var email = document.getElementById('field-email');
+ if (firstName && user.name) {
+ firstName.value = user.name;
+ }
+ if (email && user.email) {
+ email.value = user.email;
+ }
+ } catch (_error) {
+ }
+}
+
+function injectLanguageSwitcher() {
var profileSection = document.getElementById('section-profile');
- if (!profileSection) return;
+ if (!profileSection) {
+ return;
+ }
+
var cardBody = profileSection.querySelector('.section-card-body');
- if (!cardBody) return;
+ if (!cardBody) {
+ return;
+ }
+
fetch('html/fragments/lang-switcher.html')
- .then(function(r) { return r.text(); })
+ .then(function(response) { return response.text(); })
.then(function(html) {
var saveRow = cardBody.querySelector('div[style*="justify-content:flex-end"]');
if (saveRow) {
@@ -27,47 +41,128 @@ try {
} else {
cardBody.insertAdjacentHTML('beforeend', html);
}
- if (typeof applyLang === 'function') applyLang();
+ if (typeof applyLang === 'function') {
+ applyLang();
+ }
});
-})();
+}
-// Settings sidebar navigation
-var navItems = document.querySelectorAll('.settings-nav-item[data-section]');
-navItems.forEach(function(btn) {
- btn.addEventListener('click', function() {
- navItems.forEach(function(b) { b.classList.remove('active'); });
- this.classList.add('active');
- var target = document.getElementById('section-' + this.dataset.section);
- if (target) target.scrollIntoView({ behavior: 'smooth', block: 'start' });
+function bindSectionNavigation() {
+ var navItems = document.querySelectorAll('.settings-nav-item[data-section]');
+ navItems.forEach(function (button) {
+ button.addEventListener('click', function () {
+ navItems.forEach(function (item) { item.classList.remove('active'); });
+ this.classList.add('active');
+ var target = document.getElementById('section-' + this.dataset.section);
+ if (target) {
+ target.scrollIntoView({ behavior: 'smooth', block: 'start' });
+ }
+ });
});
-});
-// Hash navigation (e.g. settings.html#security)
-if (window.location.hash) {
- var hash = window.location.hash.slice(1);
- var btn = document.querySelector('.settings-nav-item[data-section="' + hash + '"]');
- if (btn) setTimeout(function() { btn.click(); }, 100);
-}
-// Default: scroll to profile
-else {
- var profileBtn = document.querySelector('.settings-nav-item[data-section="profile"]');
- if (profileBtn) profileBtn.classList.add('active');
-}
+ var initialSection = window.location.hash ? window.location.hash.slice(1) : 'profile';
+ var initialButton = document.querySelector('.settings-nav-item[data-section="' + initialSection + '"]')
+ || document.querySelector('.settings-nav-item[data-section="profile"]');
+ if (initialButton) {
+ setTimeout(function () { initialButton.click(); }, 50);
+ }
-// IntersectionObserver scroll-spy: highlight active sidebar item as user scrolls
-var observer = new IntersectionObserver(function(entries) {
- entries.forEach(function(entry) {
- if (entry.isIntersecting) {
- var sectionId = entry.target.id; // e.g. "section-profile"
- var name = sectionId.replace('section-', ''); // e.g. "profile"
- document.querySelectorAll('.settings-nav-item[data-section]').forEach(function(b) {
- b.classList.toggle('active', b.dataset.section === name);
+ var observer = new IntersectionObserver(function (entries) {
+ entries.forEach(function (entry) {
+ if (!entry.isIntersecting) {
+ return;
+ }
+ var section = entry.target.id.replace('section-', '');
+ document.querySelectorAll('.settings-nav-item[data-section]').forEach(function (button) {
+ button.classList.toggle('active', button.dataset.section === section);
});
+ });
+ }, { threshold: 0.3 });
+
+ document.querySelectorAll('[id^="section-"]').forEach(function (section) {
+ if (section.style.display !== 'none') {
+ observer.observe(section);
}
});
-}, { threshold: 0.3 });
+}
-// Observe all visible sections (not hidden ones)
-document.querySelectorAll('[id^="section-"]').forEach(function(el) {
- if (el.style.display !== 'none') observer.observe(el);
+async function loadWorkspaceSettings() {
+ if (!window.CrankApi || !window.whenWorkspacesReady) {
+ return;
+ }
+
+ await window.whenWorkspacesReady();
+ var workspace = window.getCurrentWorkspace ? window.getCurrentWorkspace() : null;
+ if (!workspace) {
+ return;
+ }
+
+ try {
+ var record = await window.CrankApi.getWorkspace(workspace.id);
+ var item = record.workspace;
+ var settings = item.settings || {};
+
+ document.getElementById('settings-ws-slug').value = item.slug || '';
+ document.getElementById('settings-ws-display-name').value = item.display_name || '';
+ document.getElementById('settings-ws-description').value = settings.description || '';
+ document.getElementById('settings-ws-timezone').value = settings.timezone || 'UTC';
+ document.getElementById('settings-ws-protocol').value = settings.default_protocol || 'rest';
+
+ document.querySelectorAll('#section-workspace .lang-btn').forEach(function (button) {
+ button.classList.toggle('active', button.dataset.lang === (settings.language || (localStorage.getItem('crank_lang') || 'en')));
+ });
+
+ var saveButton = document.getElementById('settings-ws-save-btn');
+ if (saveButton.dataset.bound === 'true') {
+ return;
+ }
+ saveButton.dataset.bound = 'true';
+
+ saveButton.addEventListener('click', async function () {
+ var saveButton = this;
+ var original = saveButton.textContent;
+ saveButton.disabled = true;
+ saveButton.textContent = 'Saving…';
+
+ try {
+ await window.CrankApi.updateWorkspace(item.id, {
+ slug: document.getElementById('settings-ws-slug').value.trim(),
+ display_name: document.getElementById('settings-ws-display-name').value.trim(),
+ settings: {
+ description: document.getElementById('settings-ws-description').value.trim(),
+ timezone: document.getElementById('settings-ws-timezone').value,
+ default_protocol: document.getElementById('settings-ws-protocol').value,
+ language: localStorage.getItem('crank_lang') || 'en',
+ color: (workspace.settings && workspace.settings.color) || '#0d9488',
+ },
+ });
+
+ if (window.refreshWorkspaces) {
+ var workspaces = await window.refreshWorkspaces();
+ var updated = workspaces.find(function (entry) { return entry.id === item.id; });
+ if (updated && window.setCurrentWorkspace) {
+ window.setCurrentWorkspace(updated);
+ }
+ }
+
+ saveButton.textContent = 'Saved ✓';
+ } catch (error) {
+ alert(error.message || 'Failed to save workspace settings');
+ saveButton.textContent = original;
+ } finally {
+ setTimeout(function () {
+ saveButton.disabled = false;
+ saveButton.textContent = original;
+ }, 1200);
+ }
+ });
+ } catch (_error) {
+ }
+}
+
+document.addEventListener('DOMContentLoaded', function () {
+ populateProfile();
+ injectLanguageSwitcher();
+ bindSectionNavigation();
+ loadWorkspaceSettings();
});
diff --git a/apps/ui/js/workspace-setup.js b/apps/ui/js/workspace-setup.js
index 6d410db..81156c7 100644
--- a/apps/ui/js/workspace-setup.js
+++ b/apps/ui/js/workspace-setup.js
@@ -1,193 +1,459 @@
-var selectedColor = '#0d9488';
-var slugManual = false;
-var isCreateMode = false;
-var formDirty = false;
+var workspaceFormState = {
+ selectedColor: '#0d9488',
+ slugManual: false,
+ isCreateMode: false,
+ formDirty: false,
+ workspaceId: null,
+ workspaceRecord: null,
+ memberships: [],
+ invitations: [],
+};
-function initPage() {
+function normalizeSlug(value) {
+ return value
+ .toLowerCase()
+ .replace(/[^a-z0-9-]/g, '')
+ .replace(/-+/g, '-')
+ .replace(/^-|-$/g, '');
+}
+
+function uiRoleToApi(value) {
+ if (value === 'developer') {
+ return 'operator';
+ }
+ return value;
+}
+
+function roleLabel(value) {
+ if (value === 'operator') {
+ return 'Developer';
+ }
+ return value.charAt(0).toUpperCase() + value.slice(1);
+}
+
+function roleClass(value) {
+ if (value === 'operator') {
+ return 'role-developer';
+ }
+ return 'role-' + value;
+}
+
+function currentLanguage() {
+ return localStorage.getItem('crank_lang') || 'en';
+}
+
+function setFormDirty(value) {
+ workspaceFormState.formDirty = value;
+}
+
+function formElements() {
+ return {
+ name: document.getElementById('ws-name'),
+ slug: document.getElementById('ws-slug'),
+ desc: document.getElementById('ws-desc'),
+ protocol: document.getElementById('ws-protocol'),
+ timezone: document.getElementById('ws-timezone'),
+ submit: document.getElementById('submit-btn'),
+ avatar: document.getElementById('ws-avatar-preview'),
+ };
+}
+
+function workspacePayload() {
+ var elements = formElements();
+ return {
+ slug: elements.slug.value.trim(),
+ display_name: elements.name.value.trim(),
+ settings: {
+ description: elements.desc.value.trim(),
+ default_protocol: elements.protocol.value,
+ timezone: elements.timezone.value,
+ language: currentLanguage(),
+ color: workspaceFormState.selectedColor,
+ },
+ };
+}
+
+function applyWorkspaceRecord(record) {
+ var workspace = record.workspace;
+ var settings = workspace.settings || {};
+ var elements = formElements();
+
+ workspaceFormState.workspaceId = workspace.id;
+ workspaceFormState.workspaceRecord = record;
+ workspaceFormState.selectedColor = settings.color || '#0d9488';
+ workspaceFormState.slugManual = true;
+
+ elements.name.value = workspace.display_name || '';
+ elements.slug.value = workspace.slug || '';
+ elements.desc.value = settings.description || '';
+ elements.protocol.value = settings.default_protocol || 'rest';
+ elements.timezone.value = settings.timezone || 'UTC';
+ elements.avatar.textContent = (workspace.display_name || workspace.slug || '?').charAt(0).toUpperCase();
+ elements.avatar.style.background = workspaceFormState.selectedColor;
+
+ document.querySelectorAll('.ws-color-swatch').forEach(function (swatch) {
+ swatch.classList.toggle('active', swatch.dataset.color === workspaceFormState.selectedColor);
+ });
+
+ setFormDirty(false);
+}
+
+function addMemberRow(container, membership) {
+ var row = document.createElement('div');
+ row.className = 'member-row';
+ row.innerHTML =
+ '
' +
+ membership.user.display_name.slice(0, 2).toUpperCase() +
+ '
' +
+ '
' +
+ '
' + membership.user.display_name + '
' +
+ '
' + membership.user.email + '
' +
+ '
' +
+ '
—
' +
+ '
' + roleLabel(membership.role) + '';
+ container.appendChild(row);
+}
+
+function renderMembers() {
+ var container = document.getElementById('members-list');
+ var label = document.getElementById('members-count-label');
+ if (!container || !label) {
+ return;
+ }
+
+ container.innerHTML = '';
+ workspaceFormState.memberships.forEach(function (membership) {
+ addMemberRow(container, membership);
+ });
+ label.textContent = workspaceFormState.memberships.length + ' member' + (workspaceFormState.memberships.length === 1 ? '' : 's');
+}
+
+function renderInvitations() {
+ var pending = document.getElementById('pending-invites');
+ var count = document.getElementById('pending-count');
+ if (!pending || !count) {
+ return;
+ }
+
+ var items = workspaceFormState.invitations.filter(function (record) {
+ return record.invitation.status === 'pending';
+ });
+
+ count.textContent = String(items.length);
+ if (!items.length) {
+ pending.style.display = 'none';
+ return;
+ }
+
+ pending.style.display = '';
+ pending.innerHTML =
+ '
' +
+ 'Pending invites ' + items.length + '' +
+ '
';
+
+ items.forEach(function (record) {
+ var invitation = record.invitation;
+ var row = document.createElement('div');
+ row.className = 'member-row';
+ row.innerHTML =
+ '
?
' +
+ '
' +
+ '
' + invitation.email + '
' +
+ '
Invited ' + invitation.created_at.slice(0, 10) + ' · pending
' +
+ '
' +
+ '
—
' +
+ '
' + roleLabel(invitation.role) + '' +
+ '
';
+ pending.appendChild(row);
+ });
+}
+
+async function loadWorkspaceAccessData() {
+ if (workspaceFormState.isCreateMode || !window.CrankApi || !workspaceFormState.workspaceId) {
+ return;
+ }
+
+ var membershipsResponse = await window.CrankApi.listMemberships(workspaceFormState.workspaceId);
+ var invitationsResponse = await window.CrankApi.listInvitations(workspaceFormState.workspaceId);
+ workspaceFormState.memberships = membershipsResponse && membershipsResponse.items ? membershipsResponse.items : [];
+ workspaceFormState.invitations = invitationsResponse && invitationsResponse.items ? invitationsResponse.items : [];
+ renderMembers();
+ renderInvitations();
+}
+
+function updatePageMode() {
var params = new URLSearchParams(window.location.search);
- isCreateMode = params.get('mode') === 'create';
+ workspaceFormState.isCreateMode = params.get('mode') === 'create';
- if (isCreateMode) {
+ var submit = document.getElementById('submit-btn');
+ if (workspaceFormState.isCreateMode) {
document.title = 'Crank — Create workspace';
document.getElementById('page-title').textContent = 'Create a new workspace';
document.getElementById('page-subtitle').textContent = 'A workspace is a shared environment for your team\'s MCP operations and agents. Each workspace gets its own MCP endpoint namespace.';
document.getElementById('section-invite').style.display = '';
document.getElementById('footer-note').style.display = '';
- var btn = document.getElementById('submit-btn');
- btn.textContent = 'Create workspace';
- btn.disabled = true;
- btn.style.opacity = '0.5';
- btn.style.cursor = 'not-allowed';
+ submit.textContent = 'Create workspace';
+ submit.disabled = true;
+ submit.style.opacity = '0.5';
+ submit.style.cursor = 'not-allowed';
} else {
- // Edit mode — prefill from current workspace
document.getElementById('section-members').style.display = '';
document.getElementById('section-danger').style.display = '';
- var ws = getCurrentWs();
- document.getElementById('ws-name').value = ws.name;
- document.getElementById('ws-slug').value = ws.slug;
- document.getElementById('ws-avatar-preview').textContent = ws.letter;
- document.getElementById('ws-avatar-preview').style.background = ws.color;
- selectedColor = ws.color;
- slugManual = true;
- // Activate matching color swatch
- document.querySelectorAll('.ws-color-swatch').forEach(function(s) {
- s.classList.toggle('active', s.dataset.color === ws.color);
- });
- // Scroll to #section-members if hash present
- if (window.location.hash === '#section-members') {
- setTimeout(function() {
- document.getElementById('section-members').scrollIntoView({ behavior: 'smooth', block: 'start' });
- }, 100);
- }
+ submit.textContent = 'Save changes';
}
+}
- // Apply language state to lang buttons
- var lang = localStorage.getItem('crank_lang') || 'en';
- document.querySelectorAll('.lang-btn').forEach(function(btn) {
- btn.classList.toggle('active', btn.dataset.lang === lang);
+function onWsNameInput(value) {
+ var elements = formElements();
+ var normalized = value.trim();
+ elements.avatar.textContent = normalized ? normalized.charAt(0).toUpperCase() : '?';
+ if (!workspaceFormState.slugManual) {
+ elements.slug.value = normalizeSlug(value);
+ }
+ if (workspaceFormState.isCreateMode) {
+ validateCreateForm();
+ }
+ setFormDirty(true);
+}
+
+function onWsSlugInput(value) {
+ formElements().slug.value = normalizeSlug(value);
+ workspaceFormState.slugManual = true;
+ if (workspaceFormState.isCreateMode) {
+ validateCreateForm();
+ }
+ setFormDirty(true);
+}
+
+function pickColor(element) {
+ workspaceFormState.selectedColor = element.dataset.color;
+ document.querySelectorAll('.ws-color-swatch').forEach(function (swatch) {
+ swatch.classList.remove('active');
});
-}
-
-function onWsNameInput(val) {
- var letter = val.trim() ? val.trim()[0].toUpperCase() : '?';
- document.getElementById('ws-avatar-preview').textContent = letter;
- if (!slugManual) {
- var slug = val.toLowerCase().replace(/\s+/g, '-').replace(/[^a-z0-9-]/g, '').replace(/-+/g, '-').replace(/^-|-$/g, '');
- document.getElementById('ws-slug').value = slug;
- }
- if (isCreateMode) validateCreateForm();
- formDirty = true;
-}
-
-function onWsSlugInput(val) {
- var clean = val.toLowerCase().replace(/[^a-z0-9-]/g, '').replace(/-+/g, '-');
- document.getElementById('ws-slug').value = clean;
- slugManual = true;
- if (isCreateMode) validateCreateForm();
- formDirty = true;
-}
-
-function pickColor(el) {
- selectedColor = el.dataset.color;
- document.querySelectorAll('.ws-color-swatch').forEach(function(s) { s.classList.remove('active'); });
- el.classList.add('active');
- document.getElementById('ws-avatar-preview').style.background = selectedColor;
- formDirty = true;
+ element.classList.add('active');
+ formElements().avatar.style.background = workspaceFormState.selectedColor;
+ setFormDirty(true);
}
function validateCreateForm() {
- var name = document.getElementById('ws-name').value.trim();
- var slug = document.getElementById('ws-slug').value.trim();
- var btn = document.getElementById('submit-btn');
- var valid = name.length > 0 && slug.length > 0;
- btn.disabled = !valid;
- btn.style.opacity = valid ? '1' : '0.5';
- btn.style.cursor = valid ? 'pointer' : 'not-allowed';
+ var elements = formElements();
+ var valid = Boolean(elements.name.value.trim() && elements.slug.value.trim());
+ elements.submit.disabled = !valid;
+ elements.submit.style.opacity = valid ? '1' : '0.5';
+ elements.submit.style.cursor = valid ? 'pointer' : 'not-allowed';
}
-function submitForm() {
- var name = document.getElementById('ws-name').value.trim();
- var slug = document.getElementById('ws-slug').value.trim();
- if (!name || !slug) return;
+function inviteRows() {
+ return Array.from(document.querySelectorAll('#invite-rows .invite-row')).map(function (row) {
+ return {
+ email: row.querySelector('input').value.trim(),
+ role: uiRoleToApi(row.querySelector('select').value),
+ };
+ }).filter(function (row) {
+ return row.email;
+ });
+}
- formDirty = false;
+async function submitForm() {
+ var payload = workspacePayload();
+ if (!payload.display_name || !payload.slug || !window.CrankApi) {
+ return;
+ }
- if (isCreateMode) {
- if (window.WS_LIST) {
- WS_LIST.push({ slug: slug, name: slug, role: 'Owner', letter: name[0].toUpperCase(), color: selectedColor });
+ var submit = document.getElementById('submit-btn');
+ var originalLabel = submit.textContent;
+ submit.disabled = true;
+ submit.textContent = workspaceFormState.isCreateMode ? 'Creating…' : 'Saving…';
+
+ try {
+ var response;
+ if (workspaceFormState.isCreateMode) {
+ response = await window.CrankApi.createWorkspace(payload);
+ } else {
+ response = await window.CrankApi.updateWorkspace(workspaceFormState.workspaceId, payload);
}
- localStorage.setItem('crank_workspace', slug);
- window.location.href = (window.APP_BASE||'')+'index.html';
- } else {
- // Update current workspace in WS_LIST
- if (window.WS_LIST) {
- var current = localStorage.getItem('crank_workspace') || 'acme-workspace';
- var idx = WS_LIST.findIndex(function(w) { return w.slug === current; });
- if (idx !== -1) {
- WS_LIST[idx].name = slug;
- WS_LIST[idx].slug = slug;
- WS_LIST[idx].letter = name[0].toUpperCase();
- WS_LIST[idx].color = selectedColor;
+
+ var record = response && response.workspace ? response : { workspace: response.workspace || response };
+ var workspace = record.workspace;
+
+ if (workspaceFormState.isCreateMode) {
+ var invites = inviteRows();
+ for (var index = 0; index < invites.length; index += 1) {
+ await window.CrankApi.createInvitation(workspace.id, {
+ email: invites[index].email,
+ role: invites[index].role,
+ });
}
}
- localStorage.setItem('crank_workspace', slug);
- // Show saved feedback
- var btn = document.getElementById('submit-btn');
- var orig = btn.textContent;
- btn.textContent = 'Saved ✓';
- btn.disabled = true;
- setTimeout(function() { btn.textContent = orig; btn.disabled = false; }, 1800);
+
+ var workspaces = await window.refreshWorkspaces();
+ var mapped = workspaces.find(function (item) { return item.id === workspace.id; }) || {
+ id: workspace.id,
+ slug: workspace.slug,
+ name: workspace.display_name,
+ role: 'Owner',
+ letter: (workspace.display_name || workspace.slug).charAt(0).toUpperCase(),
+ color: payload.settings.color || '#0d9488',
+ status: workspace.status,
+ settings: payload.settings,
+ };
+
+ window.setCurrentWorkspace(mapped);
+ workspaceFormState.workspaceId = workspace.id;
+ workspaceFormState.workspaceRecord = { workspace: workspace };
+ setFormDirty(false);
+
+ if (workspaceFormState.isCreateMode) {
+ window.location.href = (window.APP_BASE || '') + 'html/workspace-setup.html';
+ return;
+ }
+
+ applyWorkspaceRecord({ workspace: workspace });
+ await loadWorkspaceAccessData();
+ submit.textContent = 'Saved ✓';
+ setTimeout(function () {
+ submit.textContent = originalLabel;
+ submit.disabled = false;
+ }, 1400);
+ } catch (error) {
+ alert(error.message || 'Failed to save workspace');
+ submit.disabled = false;
+ submit.textContent = originalLabel;
}
}
-// Create mode invite rows
function addInviteRow() {
var rows = document.getElementById('invite-rows');
- var div = document.createElement('div');
- div.className = 'invite-row';
- div.innerHTML =
+ var node = document.createElement('div');
+ node.className = 'invite-row';
+ node.innerHTML =
'
' +
'
' +
- '