747 lines
24 KiB
JavaScript
747 lines
24 KiB
JavaScript
var workspaceFormState = {
|
|
selectedColor: '#0d9488',
|
|
slugManual: false,
|
|
isCreateMode: false,
|
|
formDirty: false,
|
|
workspaceId: null,
|
|
workspaceRecord: null,
|
|
memberships: [],
|
|
invitations: [],
|
|
currentUserId: null,
|
|
};
|
|
function tKey(key) {
|
|
return typeof t === 'function' ? t(key) : key;
|
|
}
|
|
function tfKey(key, vars) {
|
|
return typeof tf === 'function' ? tf(key, vars) : key;
|
|
}
|
|
|
|
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 tKey('workspace_setup.role.operator');
|
|
if (value === 'owner') return tKey('workspace_setup.role.owner');
|
|
if (value === 'admin') return tKey('workspace_setup.role.admin');
|
|
if (value === 'viewer') return tKey('workspace_setup.role.viewer');
|
|
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 createElement(tag, className, text) {
|
|
var node = document.createElement(tag);
|
|
if (className) {
|
|
node.className = className;
|
|
}
|
|
if (text !== undefined && text !== null) {
|
|
node.textContent = text;
|
|
}
|
|
return node;
|
|
}
|
|
|
|
function buildIconSvg(href, width, height, viewBox, attrs) {
|
|
var svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
|
|
svg.setAttribute('width', String(width));
|
|
svg.setAttribute('height', String(height));
|
|
if (viewBox) {
|
|
svg.setAttribute('viewBox', viewBox);
|
|
}
|
|
if (attrs) {
|
|
Object.keys(attrs).forEach(function(key) {
|
|
svg.setAttribute(key, attrs[key]);
|
|
});
|
|
}
|
|
var use = document.createElementNS('http://www.w3.org/2000/svg', 'use');
|
|
use.setAttribute('href', href);
|
|
svg.appendChild(use);
|
|
return svg;
|
|
}
|
|
|
|
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';
|
|
var isCurrentUser = membership.user.id === workspaceFormState.currentUserId;
|
|
var canManage = membership.role !== 'owner';
|
|
var avatar = createElement(
|
|
'div',
|
|
'member-avatar',
|
|
membership.user.display_name.slice(0, 2).toUpperCase()
|
|
);
|
|
avatar.style.background =
|
|
'linear-gradient(135deg,' + workspaceFormState.selectedColor + ',#6366f1)';
|
|
row.appendChild(avatar);
|
|
|
|
var info = createElement('div', 'member-info');
|
|
var name = createElement('div', 'member-name', membership.user.display_name);
|
|
if (isCurrentUser) {
|
|
name.appendChild(document.createTextNode(' '));
|
|
var currentUserLabel = createElement('span', null, '(you)');
|
|
currentUserLabel.style.fontSize = '11px';
|
|
currentUserLabel.style.fontWeight = '400';
|
|
currentUserLabel.style.color = 'var(--text-muted)';
|
|
name.appendChild(currentUserLabel);
|
|
}
|
|
info.appendChild(name);
|
|
info.appendChild(createElement('div', 'member-email', membership.user.email));
|
|
row.appendChild(info);
|
|
|
|
row.appendChild(createElement('div', 'member-last-active', '—'));
|
|
|
|
if (canManage) {
|
|
var select = createElement('select', 'member-role-select');
|
|
select.dataset.previous = membership.role;
|
|
[
|
|
['owner', tKey('workspace_setup.role.owner')],
|
|
['admin', tKey('workspace_setup.role.admin')],
|
|
['operator', tKey('workspace_setup.role.operator')],
|
|
['viewer', tKey('workspace_setup.role.viewer')]
|
|
].forEach(function(optionData) {
|
|
var option = document.createElement('option');
|
|
option.value = optionData[0];
|
|
option.textContent = optionData[1];
|
|
option.selected = membership.role === optionData[0];
|
|
select.appendChild(option);
|
|
});
|
|
select.addEventListener('change', function() {
|
|
updateRole(select, membership.user.id);
|
|
});
|
|
row.appendChild(select);
|
|
|
|
var removeButton = createElement('button', 'member-remove-btn');
|
|
removeButton.type = 'button';
|
|
removeButton.title = tKey('workspace_setup.members.remove');
|
|
removeButton.appendChild(
|
|
buildIconSvg(
|
|
'',
|
|
12,
|
|
12,
|
|
'0 0 16 16',
|
|
{
|
|
fill: 'none',
|
|
stroke: 'currentColor',
|
|
'stroke-width': '1.8',
|
|
'stroke-linecap': 'round',
|
|
'stroke-linejoin': 'round'
|
|
}
|
|
)
|
|
);
|
|
var removeSvg = removeButton.querySelector('svg');
|
|
var pathA = document.createElementNS('http://www.w3.org/2000/svg', 'path');
|
|
pathA.setAttribute('d', 'M2 4h12M5 4V3a1 1 0 011-1h4a1 1 0 011 1v1M10 8v4M6 8v4');
|
|
var pathB = document.createElementNS('http://www.w3.org/2000/svg', 'path');
|
|
pathB.setAttribute('d', 'M3 4l1 9a1 1 0 001 1h6a1 1 0 001-1l1-9');
|
|
removeSvg.appendChild(pathA);
|
|
removeSvg.appendChild(pathB);
|
|
removeButton.addEventListener('click', function() {
|
|
removeMember(membership.user.id, membership.user.display_name);
|
|
});
|
|
row.appendChild(removeButton);
|
|
} else {
|
|
row.appendChild(
|
|
createElement(
|
|
'span',
|
|
'member-role-badge ' + roleClass(membership.role),
|
|
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 = window.tPlural(
|
|
'workspace_setup.member_count',
|
|
workspaceFormState.memberships.length,
|
|
{ count: workspaceFormState.memberships.length }
|
|
);
|
|
}
|
|
|
|
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 = '';
|
|
var heading = createElement('div', null, tKey('workspace_setup.members.pending') + ' ');
|
|
heading.style.fontSize = '11px';
|
|
heading.style.fontWeight = '600';
|
|
heading.style.textTransform = 'uppercase';
|
|
heading.style.letterSpacing = '0.06em';
|
|
heading.style.color = 'var(--text-muted)';
|
|
heading.style.marginBottom = '10px';
|
|
var countPill = createElement('span', null, String(items.length));
|
|
countPill.id = 'pending-count';
|
|
countPill.style.background = 'rgba(139,148,158,0.15)';
|
|
countPill.style.borderRadius = '10px';
|
|
countPill.style.padding = '1px 7px';
|
|
countPill.style.fontSize = '10px';
|
|
heading.appendChild(countPill);
|
|
pending.appendChild(heading);
|
|
|
|
items.forEach(function (record) {
|
|
var invitation = record.invitation;
|
|
var row = document.createElement('div');
|
|
row.className = 'member-row';
|
|
var inviteAvatar = createElement('div', 'member-avatar', '?');
|
|
inviteAvatar.style.background = 'var(--bg-canvas)';
|
|
inviteAvatar.style.border = '1.5px dashed var(--border)';
|
|
inviteAvatar.style.color = 'var(--text-muted)';
|
|
inviteAvatar.style.fontSize = '16px';
|
|
row.appendChild(inviteAvatar);
|
|
|
|
var inviteInfo = createElement('div', 'member-info');
|
|
inviteInfo.appendChild(createElement('div', 'member-name', invitation.email));
|
|
inviteInfo.appendChild(
|
|
createElement(
|
|
'div',
|
|
'member-email',
|
|
tfKey('workspace_setup.members.invited_pending', { date: invitation.created_at.slice(0, 10) })
|
|
)
|
|
);
|
|
row.appendChild(inviteInfo);
|
|
row.appendChild(createElement('div', 'member-last-active', '—'));
|
|
|
|
var roleBadge = createElement('span', 'member-role-badge', roleLabel(invitation.role));
|
|
roleBadge.style.background = 'rgba(139,148,158,0.12)';
|
|
roleBadge.style.color = 'var(--text-muted)';
|
|
roleBadge.style.border = '1px solid var(--border)';
|
|
row.appendChild(roleBadge);
|
|
|
|
var revokeButton = createElement('button', 'member-remove-btn');
|
|
revokeButton.type = 'button';
|
|
revokeButton.title = tKey('workspace_setup.members.revoke');
|
|
var revokeSvg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
|
|
revokeSvg.setAttribute('width', '12');
|
|
revokeSvg.setAttribute('height', '12');
|
|
revokeSvg.setAttribute('viewBox', '0 0 16 16');
|
|
revokeSvg.setAttribute('fill', 'none');
|
|
revokeSvg.setAttribute('stroke', 'currentColor');
|
|
revokeSvg.setAttribute('stroke-width', '1.8');
|
|
revokeSvg.setAttribute('stroke-linecap', 'round');
|
|
var lineA = document.createElementNS('http://www.w3.org/2000/svg', 'line');
|
|
lineA.setAttribute('x1', '2');
|
|
lineA.setAttribute('y1', '2');
|
|
lineA.setAttribute('x2', '14');
|
|
lineA.setAttribute('y2', '14');
|
|
var lineB = document.createElementNS('http://www.w3.org/2000/svg', 'line');
|
|
lineB.setAttribute('x1', '14');
|
|
lineB.setAttribute('y1', '2');
|
|
lineB.setAttribute('x2', '2');
|
|
lineB.setAttribute('y2', '14');
|
|
revokeSvg.appendChild(lineA);
|
|
revokeSvg.appendChild(lineB);
|
|
revokeButton.appendChild(revokeSvg);
|
|
revokeButton.addEventListener('click', function() {
|
|
revokeInviteById(invitation.id);
|
|
});
|
|
row.appendChild(revokeButton);
|
|
pending.appendChild(row);
|
|
});
|
|
}
|
|
|
|
async function loadWorkspaceAccessData() {
|
|
if (workspaceFormState.isCreateMode || !window.CrankApi || !workspaceFormState.workspaceId) {
|
|
return;
|
|
}
|
|
|
|
if (window.CrankAuth && typeof window.CrankAuth.fetchSession === 'function') {
|
|
try {
|
|
var session = await window.CrankAuth.fetchSession(false);
|
|
workspaceFormState.currentUserId = session && session.user ? session.user.id : null;
|
|
} catch (_error) {}
|
|
}
|
|
|
|
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);
|
|
workspaceFormState.isCreateMode = params.get('mode') === 'create';
|
|
|
|
var submit = document.getElementById('submit-btn');
|
|
if (workspaceFormState.isCreateMode) {
|
|
document.title = 'Crank — ' + tKey('workspace_setup.create.title');
|
|
document.getElementById('page-title').textContent = tKey('workspace_setup.create.title');
|
|
document.getElementById('page-subtitle').textContent = tKey('workspace_setup.create.subtitle');
|
|
document.getElementById('section-invite').style.display = '';
|
|
document.getElementById('footer-note').style.display = '';
|
|
submit.textContent = tKey('workspace_setup.actions.create');
|
|
submit.disabled = true;
|
|
submit.classList.add('is-disabled');
|
|
} else {
|
|
document.getElementById('section-members').style.display = '';
|
|
document.getElementById('section-danger').style.display = '';
|
|
submit.textContent = tKey('workspace_setup.actions.save');
|
|
submit.classList.remove('is-disabled');
|
|
}
|
|
}
|
|
|
|
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');
|
|
});
|
|
element.classList.add('active');
|
|
formElements().avatar.style.background = workspaceFormState.selectedColor;
|
|
setFormDirty(true);
|
|
}
|
|
|
|
function validateCreateForm() {
|
|
var elements = formElements();
|
|
var valid = Boolean(elements.name.value.trim() && elements.slug.value.trim());
|
|
elements.submit.disabled = !valid;
|
|
elements.submit.classList.toggle('is-disabled', !valid);
|
|
}
|
|
|
|
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;
|
|
});
|
|
}
|
|
|
|
async function submitForm() {
|
|
var payload = workspacePayload();
|
|
if (!payload.display_name || !payload.slug || !window.CrankApi) {
|
|
return;
|
|
}
|
|
|
|
var submit = document.getElementById('submit-btn');
|
|
var originalLabel = submit.textContent;
|
|
submit.disabled = true;
|
|
submit.textContent = workspaceFormState.isCreateMode ? tKey('workspace_setup.saving_create') : tKey('workspace_setup.saving_update');
|
|
|
|
try {
|
|
var response;
|
|
if (workspaceFormState.isCreateMode) {
|
|
response = await window.CrankApi.createWorkspace(payload);
|
|
} else {
|
|
response = await window.CrankApi.updateWorkspace(workspaceFormState.workspaceId, payload);
|
|
}
|
|
|
|
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,
|
|
});
|
|
}
|
|
}
|
|
|
|
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: tKey('workspace_setup.role.owner'),
|
|
letter: (workspace.display_name || workspace.slug).charAt(0).toUpperCase(),
|
|
color: payload.settings.color || '#0d9488',
|
|
status: workspace.status,
|
|
settings: payload.settings,
|
|
};
|
|
|
|
await window.setCurrentWorkspace(mapped);
|
|
workspaceFormState.workspaceId = workspace.id;
|
|
workspaceFormState.workspaceRecord = { workspace: workspace };
|
|
setFormDirty(false);
|
|
|
|
if (workspaceFormState.isCreateMode) {
|
|
window.location.href = (window.CrankRoutes && window.CrankRoutes.workspaceSetup) || '/workspace-setup';
|
|
return;
|
|
}
|
|
|
|
applyWorkspaceRecord({ workspace: workspace });
|
|
await loadWorkspaceAccessData();
|
|
submit.textContent = tKey('workspace_setup.saved');
|
|
setTimeout(function () {
|
|
submit.textContent = originalLabel;
|
|
submit.disabled = false;
|
|
}, 1400);
|
|
} catch (error) {
|
|
if (window.CrankUi) {
|
|
window.CrankUi.error(error.message || tKey('workspace_setup.save_error'), tKey('workspace_setup.save_error_title'));
|
|
}
|
|
submit.disabled = false;
|
|
submit.textContent = originalLabel;
|
|
}
|
|
}
|
|
|
|
function addInviteRow() {
|
|
var rows = document.getElementById('invite-rows');
|
|
var node = document.createElement('div');
|
|
node.className = 'invite-row';
|
|
var emailInput = createElement('input', 'form-input');
|
|
emailInput.type = 'email';
|
|
emailInput.placeholder = 'colleague@company.com';
|
|
emailInput.autocomplete = 'off';
|
|
emailInput.style.flex = '1';
|
|
emailInput.style.marginBottom = '0';
|
|
node.appendChild(emailInput);
|
|
|
|
var roleSelect = createElement('select', 'form-input');
|
|
roleSelect.style.width = '130px';
|
|
roleSelect.style.marginBottom = '0';
|
|
[
|
|
['admin', tKey('workspace_setup.role.admin')],
|
|
['developer', tKey('workspace_setup.role.operator')],
|
|
['viewer', tKey('workspace_setup.role.viewer')]
|
|
].forEach(function(entry) {
|
|
var option = document.createElement('option');
|
|
option.value = entry[0];
|
|
option.textContent = entry[1];
|
|
option.selected = entry[0] === 'developer';
|
|
roleSelect.appendChild(option);
|
|
});
|
|
node.appendChild(roleSelect);
|
|
|
|
var removeButton = createElement('button', 'invite-row-remove');
|
|
removeButton.type = 'button';
|
|
removeButton.title = tKey('workspace_setup.members.remove');
|
|
removeButton.appendChild(
|
|
buildIconSvg(
|
|
(window.APP_BASE || '') + 'icons/general/close.svg#icon',
|
|
12,
|
|
12
|
|
)
|
|
);
|
|
removeButton.addEventListener('click', function() {
|
|
removeInviteRow(removeButton);
|
|
});
|
|
node.appendChild(removeButton);
|
|
rows.appendChild(node);
|
|
}
|
|
|
|
function removeInviteRow(button) {
|
|
button.closest('.invite-row').remove();
|
|
}
|
|
|
|
function toggleInviteForm() {
|
|
var form = document.getElementById('invite-form');
|
|
var success = document.getElementById('invite-success');
|
|
if (!form) {
|
|
return;
|
|
}
|
|
|
|
if (form.style.display === 'none') {
|
|
form.style.display = '';
|
|
if (success) {
|
|
success.style.display = 'none';
|
|
}
|
|
document.getElementById('invite-email').focus();
|
|
} else {
|
|
form.style.display = 'none';
|
|
}
|
|
}
|
|
|
|
async function sendInvite() {
|
|
if (!window.CrankApi || !workspaceFormState.workspaceId) {
|
|
return;
|
|
}
|
|
|
|
var email = document.getElementById('invite-email').value.trim();
|
|
var role = uiRoleToApi(document.getElementById('invite-role').value);
|
|
if (!email || email.indexOf('@') === -1) {
|
|
return;
|
|
}
|
|
|
|
try {
|
|
await window.CrankApi.createInvitation(workspaceFormState.workspaceId, { email: email, role: role });
|
|
document.getElementById('invite-email').value = '';
|
|
document.getElementById('invite-success').style.display = '';
|
|
await loadWorkspaceAccessData();
|
|
} catch (error) {
|
|
if (window.CrankUi) {
|
|
window.CrankUi.error(error.message || tKey('workspace_setup.invite_error'), tKey('workspace_setup.invite_error_title'));
|
|
}
|
|
}
|
|
}
|
|
|
|
async function updateRole(select, userId) {
|
|
if (!workspaceFormState.workspaceId || !window.CrankApi) {
|
|
return;
|
|
}
|
|
|
|
var previous = select.dataset.previous || select.value;
|
|
select.disabled = true;
|
|
|
|
try {
|
|
var response = await window.CrankApi.updateMembership(workspaceFormState.workspaceId, userId, {
|
|
role: select.value,
|
|
});
|
|
workspaceFormState.memberships = response && response.items ? response.items : workspaceFormState.memberships;
|
|
renderMembers();
|
|
} catch (error) {
|
|
select.value = previous;
|
|
if (window.CrankUi) {
|
|
window.CrankUi.error(error.message || tKey('workspace_setup.role_error'), tKey('workspace_setup.role_error_title'));
|
|
}
|
|
} finally {
|
|
select.disabled = false;
|
|
}
|
|
}
|
|
|
|
async function removeMember(userId, name) {
|
|
if (!workspaceFormState.workspaceId || !window.CrankApi) {
|
|
return;
|
|
}
|
|
if (!confirm(tfKey('workspace_setup.remove_confirm', { name: name }))) {
|
|
return;
|
|
}
|
|
|
|
try {
|
|
await window.CrankApi.deleteMembership(workspaceFormState.workspaceId, userId);
|
|
await loadWorkspaceAccessData();
|
|
} catch (error) {
|
|
if (window.CrankUi) {
|
|
window.CrankUi.error(error.message || tKey('workspace_setup.remove_error'), tKey('workspace_setup.remove_error_title'));
|
|
}
|
|
}
|
|
}
|
|
|
|
async function revokeInviteById(invitationId) {
|
|
if (!window.CrankApi || !workspaceFormState.workspaceId) {
|
|
return;
|
|
}
|
|
|
|
try {
|
|
await window.CrankApi.deleteInvitation(workspaceFormState.workspaceId, invitationId);
|
|
await loadWorkspaceAccessData();
|
|
} catch (error) {
|
|
if (window.CrankUi) {
|
|
window.CrankUi.error(error.message || tKey('workspace_setup.revoke_error'), tKey('workspace_setup.revoke_error_title'));
|
|
}
|
|
}
|
|
}
|
|
|
|
function revokeInvite(button) {
|
|
var row = button.closest('.member-row');
|
|
if (!row) {
|
|
return;
|
|
}
|
|
row.remove();
|
|
}
|
|
|
|
function downloadJsonFile(fileName, value) {
|
|
var blob = new Blob([JSON.stringify(value, null, 2)], { type: 'application/json;charset=utf-8' });
|
|
var url = URL.createObjectURL(blob);
|
|
var link = document.createElement('a');
|
|
link.href = url;
|
|
link.download = fileName;
|
|
link.click();
|
|
URL.revokeObjectURL(url);
|
|
}
|
|
|
|
async function exportWorkspaceSnapshot() {
|
|
if (!workspaceFormState.workspaceId || !window.CrankApi) {
|
|
return;
|
|
}
|
|
|
|
try {
|
|
var snapshot = await window.CrankApi.exportWorkspace(workspaceFormState.workspaceId);
|
|
var slug = workspaceFormState.workspaceRecord && workspaceFormState.workspaceRecord.workspace
|
|
? workspaceFormState.workspaceRecord.workspace.slug
|
|
: tKey('settings.nav.workspace');
|
|
downloadJsonFile(slug + '-snapshot.json', snapshot);
|
|
} catch (error) {
|
|
if (window.CrankUi) {
|
|
window.CrankUi.error(error.message || tKey('workspace_setup.export_error'), tKey('workspace_setup.export_error_title'));
|
|
}
|
|
}
|
|
}
|
|
|
|
async function deleteWorkspaceAction() {
|
|
if (!workspaceFormState.workspaceId || !window.CrankApi || !window.CrankAuth) {
|
|
return;
|
|
}
|
|
if (!confirm(tKey('workspace_setup.delete_confirm'))) {
|
|
return;
|
|
}
|
|
|
|
try {
|
|
await window.CrankApi.deleteWorkspace(workspaceFormState.workspaceId);
|
|
await window.CrankAuth.logout();
|
|
} catch (error) {
|
|
if (window.CrankUi) {
|
|
window.CrankUi.error(error.message || tKey('workspace_setup.delete_error'), tKey('workspace_setup.delete_error_title'));
|
|
}
|
|
}
|
|
}
|
|
|
|
async function initPage() {
|
|
updatePageMode();
|
|
|
|
var language = currentLanguage();
|
|
document.querySelectorAll('.lang-btn').forEach(function (button) {
|
|
button.classList.toggle('active', button.dataset.lang === language);
|
|
});
|
|
|
|
document.getElementById('export-workspace-btn').addEventListener('click', exportWorkspaceSnapshot);
|
|
document.getElementById('delete-workspace-btn').addEventListener('click', deleteWorkspaceAction);
|
|
|
|
if (workspaceFormState.isCreateMode) {
|
|
document.getElementById('ws-name').focus();
|
|
return;
|
|
}
|
|
|
|
await window.whenWorkspacesReady();
|
|
var workspace = window.getCurrentWorkspace ? window.getCurrentWorkspace() : null;
|
|
if (!workspace || !window.CrankApi) {
|
|
return;
|
|
}
|
|
|
|
try {
|
|
var record = await window.CrankApi.getWorkspace(workspace.id);
|
|
applyWorkspaceRecord(record);
|
|
await loadWorkspaceAccessData();
|
|
} catch (error) {
|
|
if (window.CrankUi) {
|
|
window.CrankUi.error(error.message || 'Failed to load workspace', 'Workspace load failed');
|
|
}
|
|
}
|
|
}
|
|
|
|
document.addEventListener('DOMContentLoaded', initPage);
|
|
|
|
window.addEventListener('beforeunload', function (event) {
|
|
if (workspaceFormState.formDirty) {
|
|
event.preventDefault();
|
|
event.returnValue = '';
|
|
}
|
|
});
|