feat: complete Epic 1 production foundation
This commit is contained in:
+89
-15
@@ -13,6 +13,10 @@ function initSecretsPage() {
|
||||
error: '',
|
||||
modalMode: 'create',
|
||||
modalSecretId: null,
|
||||
modalSubmitting: false,
|
||||
deleting: {},
|
||||
generation: 0,
|
||||
modalGeneration: 0,
|
||||
};
|
||||
|
||||
var modal = document.getElementById('secret-modal');
|
||||
@@ -107,6 +111,10 @@ function initSecretsPage() {
|
||||
}
|
||||
|
||||
function openModal(mode, secret) {
|
||||
if (state.modalSubmitting) {
|
||||
return;
|
||||
}
|
||||
state.modalGeneration += 1;
|
||||
state.modalMode = mode;
|
||||
state.modalSecretId = secret ? secret.id : null;
|
||||
resetModalFields();
|
||||
@@ -143,8 +151,16 @@ function initSecretsPage() {
|
||||
|
||||
function closeModal() {
|
||||
modal.classList.remove('open');
|
||||
state.modalGeneration += 1;
|
||||
state.modalSubmitting = false;
|
||||
state.modalMode = 'create';
|
||||
state.modalSecretId = null;
|
||||
resetModalFields();
|
||||
modalName.disabled = false;
|
||||
modalKind.disabled = false;
|
||||
modalSubmit.disabled = state.modalSubmitting;
|
||||
modalSubmit.textContent = tKey('secrets.modal.create_action');
|
||||
renderSecrets();
|
||||
}
|
||||
|
||||
function updateKindFields() {
|
||||
@@ -290,6 +306,7 @@ function initSecretsPage() {
|
||||
rotateButton.textContent = tKey('secrets.action.rotate');
|
||||
rotateButton.setAttribute('data-testid', 'secret-rotate-action');
|
||||
rotateButton.setAttribute('data-secret-id', secret.id);
|
||||
rotateButton.disabled = Boolean(state.deleting[secret.id]) || state.modalSubmitting;
|
||||
rotateButton.addEventListener('click', function () {
|
||||
openModal('rotate', secret);
|
||||
});
|
||||
@@ -301,6 +318,7 @@ function initSecretsPage() {
|
||||
deleteButton.textContent = tKey('secrets.action.delete');
|
||||
deleteButton.setAttribute('data-testid', 'secret-delete-action');
|
||||
deleteButton.setAttribute('data-secret-id', secret.id);
|
||||
deleteButton.disabled = Boolean(state.deleting[secret.id]) || state.modalSubmitting;
|
||||
deleteButton.addEventListener('click', async function () {
|
||||
await deleteSecret(secret);
|
||||
});
|
||||
@@ -378,6 +396,7 @@ function initSecretsPage() {
|
||||
rotateButton.className = 'btn-secondary';
|
||||
rotateButton.type = 'button';
|
||||
rotateButton.textContent = tKey('secrets.action.rotate');
|
||||
rotateButton.disabled = Boolean(state.deleting[secret.id]) || state.modalSubmitting;
|
||||
rotateButton.addEventListener('click', function() {
|
||||
openModal('rotate', secret);
|
||||
});
|
||||
@@ -387,6 +406,7 @@ function initSecretsPage() {
|
||||
deleteButton.className = 'btn-secondary';
|
||||
deleteButton.type = 'button';
|
||||
deleteButton.textContent = tKey('secrets.action.delete');
|
||||
deleteButton.disabled = Boolean(state.deleting[secret.id]) || state.modalSubmitting;
|
||||
deleteButton.addEventListener('click', async function() {
|
||||
await deleteSecret(secret);
|
||||
});
|
||||
@@ -412,12 +432,14 @@ function initSecretsPage() {
|
||||
}
|
||||
|
||||
async function load() {
|
||||
state.workspaceId = currentWorkspaceId();
|
||||
var workspaceId = currentWorkspaceId();
|
||||
var generation = ++state.generation;
|
||||
state.workspaceId = workspaceId;
|
||||
state.loading = true;
|
||||
state.error = '';
|
||||
renderSecrets();
|
||||
|
||||
if (!state.workspaceId) {
|
||||
if (!workspaceId) {
|
||||
state.secrets = [];
|
||||
state.profiles = [];
|
||||
recomputeDerivedState();
|
||||
@@ -429,49 +451,79 @@ function initSecretsPage() {
|
||||
|
||||
try {
|
||||
var results = await Promise.all([
|
||||
window.CrankApi.listSecrets(state.workspaceId),
|
||||
window.CrankApi.listAuthProfiles(state.workspaceId),
|
||||
window.CrankApi.listSecrets(workspaceId),
|
||||
window.CrankApi.listAuthProfiles(workspaceId),
|
||||
]);
|
||||
if (generation !== state.generation || workspaceId !== currentWorkspaceId()) {
|
||||
return;
|
||||
}
|
||||
state.secrets = (results[0] && results[0].items) || [];
|
||||
state.profiles = (results[1] && results[1].items) || [];
|
||||
recomputeDerivedState();
|
||||
} catch (error) {
|
||||
if (generation !== state.generation || workspaceId !== currentWorkspaceId()) {
|
||||
return;
|
||||
}
|
||||
state.error = error.message || tKey('secrets.error.load');
|
||||
state.secrets = [];
|
||||
state.profiles = [];
|
||||
recomputeDerivedState();
|
||||
} finally {
|
||||
if (generation !== state.generation || workspaceId !== currentWorkspaceId()) {
|
||||
return;
|
||||
}
|
||||
state.loading = false;
|
||||
renderSecrets();
|
||||
}
|
||||
}
|
||||
|
||||
async function deleteSecret(secret) {
|
||||
if (state.deleting[secret.id]) {
|
||||
return;
|
||||
}
|
||||
if (!confirm(tfKey('secrets.confirm.delete', { name: secret.name }))) {
|
||||
return;
|
||||
}
|
||||
var workspaceId = state.workspaceId;
|
||||
state.deleting[secret.id] = true;
|
||||
renderSecrets();
|
||||
try {
|
||||
await window.CrankApi.deleteSecret(state.workspaceId, secret.id);
|
||||
await load();
|
||||
if (window.CrankUi) {
|
||||
await window.CrankApi.deleteSecret(workspaceId, secret.id);
|
||||
if (workspaceId === currentWorkspaceId()) {
|
||||
await load();
|
||||
}
|
||||
if (window.CrankUi && workspaceId === currentWorkspaceId()) {
|
||||
window.CrankUi.success(
|
||||
tfKey('secrets.toast.delete_message', { name: secret.name }),
|
||||
tKey('secrets.toast.delete_title')
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
if (window.CrankUi) {
|
||||
if (window.CrankUi && workspaceId === currentWorkspaceId()) {
|
||||
window.CrankUi.error(
|
||||
error.message || tKey('secrets.toast.delete_error_message'),
|
||||
tKey('secrets.toast.delete_error_title')
|
||||
);
|
||||
}
|
||||
} finally {
|
||||
delete state.deleting[secret.id];
|
||||
if (workspaceId === currentWorkspaceId()) {
|
||||
renderSecrets();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function submitModal() {
|
||||
if (state.modalSubmitting) {
|
||||
return;
|
||||
}
|
||||
var kind = modalKind.value;
|
||||
var value = buildSecretValue();
|
||||
var workspaceId = state.workspaceId;
|
||||
var secretId = state.modalSecretId;
|
||||
var submissionGeneration = ++state.modalGeneration;
|
||||
var submissionMode = state.modalMode;
|
||||
state.modalSubmitting = true;
|
||||
modalSubmit.disabled = true;
|
||||
modalSubmit.textContent = state.modalMode === 'rotate'
|
||||
? tKey('secrets.modal.rotating')
|
||||
@@ -479,7 +531,16 @@ function initSecretsPage() {
|
||||
|
||||
try {
|
||||
if (state.modalMode === 'rotate') {
|
||||
await window.CrankApi.rotateSecret(state.workspaceId, state.modalSecretId, { value: value });
|
||||
await window.CrankApi.rotateSecret(workspaceId, secretId, { value: value });
|
||||
if (
|
||||
submissionGeneration !== state.modalGeneration
|
||||
|| workspaceId !== currentWorkspaceId()
|
||||
|| state.modalSecretId !== secretId
|
||||
|| submissionMode !== state.modalMode
|
||||
|| !modal.classList.contains('open')
|
||||
) {
|
||||
return;
|
||||
}
|
||||
if (window.CrankUi) {
|
||||
window.CrankUi.success(
|
||||
tKey('secrets.toast.rotate_message'),
|
||||
@@ -491,11 +552,19 @@ function initSecretsPage() {
|
||||
if (!name) {
|
||||
throw new Error(tKey('secrets.validation.name_required'));
|
||||
}
|
||||
await window.CrankApi.createSecret(state.workspaceId, {
|
||||
await window.CrankApi.createSecret(workspaceId, {
|
||||
name: name,
|
||||
kind: kind,
|
||||
value: value,
|
||||
});
|
||||
if (
|
||||
submissionGeneration !== state.modalGeneration
|
||||
|| workspaceId !== currentWorkspaceId()
|
||||
|| submissionMode !== state.modalMode
|
||||
|| !modal.classList.contains('open')
|
||||
) {
|
||||
return;
|
||||
}
|
||||
if (window.CrankUi) {
|
||||
window.CrankUi.success(
|
||||
tKey('secrets.toast.create_message'),
|
||||
@@ -506,17 +575,20 @@ function initSecretsPage() {
|
||||
closeModal();
|
||||
await load();
|
||||
} catch (error) {
|
||||
if (window.CrankUi) {
|
||||
if (window.CrankUi && submissionGeneration === state.modalGeneration) {
|
||||
window.CrankUi.error(
|
||||
error.message || tKey(state.modalMode === 'rotate' ? 'secrets.toast.rotate_error_message' : 'secrets.toast.create_error_message'),
|
||||
tKey(state.modalMode === 'rotate' ? 'secrets.toast.rotate_error_title' : 'secrets.toast.create_error_title')
|
||||
);
|
||||
}
|
||||
} finally {
|
||||
modalSubmit.disabled = false;
|
||||
modalSubmit.textContent = state.modalMode === 'rotate'
|
||||
? tKey('secrets.modal.rotate_action')
|
||||
: tKey('secrets.modal.create_action');
|
||||
if (submissionGeneration === state.modalGeneration) {
|
||||
state.modalSubmitting = false;
|
||||
modalSubmit.disabled = false;
|
||||
modalSubmit.textContent = state.modalMode === 'rotate'
|
||||
? tKey('secrets.modal.rotate_action')
|
||||
: tKey('secrets.modal.create_action');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -539,6 +611,8 @@ function initSecretsPage() {
|
||||
renderSecrets();
|
||||
});
|
||||
window.addEventListener('crank:workspacechange', function () {
|
||||
closeModal();
|
||||
state.deleting = {};
|
||||
void load();
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user