Fix workspace selection and secret modal layout
Deploy / deploy (push) Failing after 1m43s
CI / Rust Checks (push) Successful in 5m54s
CI / UI Checks (push) Successful in 4s
CI / Deployment Manifests (push) Successful in 3s
CI / Frontend E2E (push) Successful in 4m24s

This commit is contained in:
github-ops
2026-06-22 18:30:56 +00:00
parent 3472d06a70
commit 5f8149d0d1
14 changed files with 286 additions and 43 deletions
+34
View File
@@ -154,6 +154,40 @@
max-width: 1160px;
margin: 0 auto;
padding: 36px 40px 60px;
animation: page-enter 0.18s ease-out both;
transition: opacity 0.12s ease, transform 0.12s ease;
}
body.page-leaving .page,
body.page-leaving .wizard-body,
body.page-leaving .ws-setup-body {
opacity: 0;
transform: translateY(6px);
}
@keyframes page-enter {
from {
opacity: 0;
transform: translateY(8px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@media (prefers-reduced-motion: reduce) {
.page {
animation: none;
transition: none;
}
body.page-leaving .page,
body.page-leaving .wizard-body,
body.page-leaving .ws-setup-body {
opacity: 1;
transform: none;
}
}
/* ── Hamburger button (hidden on desktop) ── */
+5 -2
View File
@@ -767,7 +767,10 @@
border-radius: 12px;
width: 100%;
max-width: 480px;
max-height: calc(100dvh - 48px);
box-shadow: 0 8px 40px rgba(0,0,0,0.6);
display: flex;
flex-direction: column;
overflow: hidden;
}
@@ -801,8 +804,8 @@
}
.modal-close:hover { background: var(--bg-overlay); color: var(--text-primary); }
.modal-body { padding: 20px; }
.modal-footer { padding: 14px 20px; border-top: 1px solid var(--border-subtle); display: flex; justify-content: flex-end; gap: 8px; }
.modal-body { padding: 20px; overflow: auto; }
.modal-footer { padding: 14px 20px; border-top: 1px solid var(--border-subtle); display: flex; justify-content: flex-end; gap: 8px; flex-shrink: 0; }
/* ══════════════════════════════════════════════════
RESPONSIVE
+2
View File
@@ -153,6 +153,8 @@
padding: 40px 40px 140px;
gap: 36px;
align-items: flex-start;
animation: page-enter 0.18s ease-out both;
transition: opacity 0.12s ease, transform 0.12s ease;
}
.checkbox-pill {
+2
View File
@@ -47,6 +47,8 @@
display: flex;
justify-content: center;
padding: 48px 24px 80px;
animation: page-enter 0.18s ease-out both;
transition: opacity 0.12s ease, transform 0.12s ease;
}
.ws-setup-container {
width: 100%;
+5
View File
@@ -19,6 +19,11 @@
color: var(--text-muted);
line-height: 1.55;
}
#secret-json-value {
min-height: 132px;
max-height: 220px;
resize: vertical;
}
.secrets-card-list { display: none; }
@media (max-width: 720px) {
#secrets-table-wrap { display: none; }
+54
View File
@@ -0,0 +1,54 @@
(function() {
'use strict';
var transitionMs = 120;
function prefersReducedMotion() {
return window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;
}
function isInternalNavigation(link) {
if (!link || !link.href || link.target || link.hasAttribute('download')) return false;
var targetUrl;
try {
targetUrl = new URL(link.href, window.location.href);
} catch (_error) {
return false;
}
if (targetUrl.origin !== window.location.origin) return false;
if (targetUrl.pathname === window.location.pathname && targetUrl.search === window.location.search) {
return targetUrl.hash && targetUrl.hash !== window.location.hash;
}
return true;
}
function initPageTransitions() {
if (prefersReducedMotion()) return;
document.addEventListener('click', function(event) {
if (event.defaultPrevented || event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) {
return;
}
var link = event.target.closest ? event.target.closest('a[href]') : null;
if (!isInternalNavigation(link)) return;
var targetUrl = new URL(link.href, window.location.href);
if (targetUrl.pathname === window.location.pathname && targetUrl.search === window.location.search) return;
event.preventDefault();
document.body.classList.add('page-leaving');
window.setTimeout(function() {
window.location.href = targetUrl.href;
}, transitionMs);
});
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initPageTransitions, { once: true });
} else {
initPageTransitions();
}
})();
+5 -2
View File
@@ -538,12 +538,15 @@ function initSecretsPage() {
state.search = event.target.value || '';
renderSecrets();
});
document.addEventListener('workspace:changed', function () {
window.addEventListener('crank:workspacechange', function () {
void load();
});
updateKindFields();
void load();
void (async function bootSecretsPage() {
await (window.whenWorkspacesReady ? window.whenWorkspacesReady() : Promise.resolve());
await load();
}());
}
if (window.CrankDiagnostics && typeof window.CrankDiagnostics.bootstrap === 'function') {
+30 -5
View File
@@ -1,6 +1,4 @@
var WS_LIST = [
{ id: 'ws_default', slug: 'default', name: 'Default workspace', role: 'Owner', letter: 'D', color: '#0d9488' }
];
var WS_LIST = [];
var workspaceLoadPromise = null;
var workspaceLoadFailed = false;
@@ -41,8 +39,35 @@ function cacheCurrentWorkspace(workspace) {
} catch (_error) {}
}
function storedWorkspaceId() {
try {
return localStorage.getItem('crank_workspace_id') || '';
} catch (_error) {
return '';
}
}
function sessionWorkspaceId() {
if (!(window.CrankAuth && typeof window.CrankAuth.getCachedSession === 'function')) {
return '';
}
var session = window.CrankAuth.getCachedSession();
return session && session.current_workspace_id ? session.current_workspace_id : '';
}
function resolveCurrentWorkspace() {
return WS_LIST[0] || null;
var preferredId = sessionWorkspaceId() || storedWorkspaceId();
if (preferredId) {
var preferred = WS_LIST.find(function(workspace) {
return workspace.id === preferredId;
});
if (preferred) {
return preferred;
}
}
return WS_LIST.find(function(workspace) {
return workspace.id === 'ws_default';
}) || WS_LIST[0] || null;
}
function updateWorkspaceHeader(workspace) {
@@ -88,7 +113,7 @@ async function loadWorkspaces() {
var response = await window.CrankApi.listWorkspaces();
var items = (response && response.items ? response.items : []).map(mapWorkspace);
if (items.length > 0) {
WS_LIST = [items[0]];
WS_LIST = items;
}
workspaceLoadFailed = false;
} catch (_error) {
+1
View File
@@ -20,6 +20,7 @@ const BUNDLES = {
'js/overlay-loader.js',
'js/dom.js',
'js/diagnostics.js',
'js/page-transitions.js',
'js/workspace.js',
'js/api.js',
'js/ui-feedback.js',
+19
View File
@@ -65,3 +65,22 @@ test('secret modal shows fields for selected secret kind only', async ({ page })
await expect(basicField).toBeHidden();
await expect(jsonField).toBeHidden();
});
test('generic json secret modal stays inside compact viewport', async ({ page }) => {
await page.setViewportSize({ width: 900, height: 520 });
await login(page);
await page.goto('/secrets');
await page.locator('[data-testid="secret-create-button"]').click();
await page.locator('[data-testid="secret-kind-select"]').selectOption('generic');
const modal = page.locator('[data-testid="secret-create-modal"] .modal');
await expect(modal).toBeVisible();
const box = await modal.boundingBox();
expect(box).not.toBeNull();
expect(box.y).toBeGreaterThanOrEqual(0);
expect(box.y + box.height).toBeLessThanOrEqual(520);
await expect(page.locator('[data-testid="secret-name-input"]')).toBeVisible();
await expect(page.locator('[data-testid="secret-submit-button"]')).toBeVisible();
});