diff --git a/frontend/README.md b/frontend/README.md index 236edc1f5..2e7200bbd 100644 --- a/frontend/README.md +++ b/frontend/README.md @@ -344,6 +344,13 @@ See [deployment and operation](service/studio_release_notifier/README.md). versions; any version can be restored into the intelligent-development flow for another intent-driven iteration after the temporary migration environment has ended. + The workspace is a single-column page without a side navigation: the home is + the upload entry followed by recent migrations (five by default, expandable + in place, with expired environments offered as view-only), “已迁移项目” is a + top-bar entry that opens the saved-project library, and returning home or + opening the project library never stops a running migration. Capability and + session-list failures are reported separately so the upload entry stays + usable and each failure offers its own retry. Pencil icons beside project and version names open the existing-style name dialog; the check icon saves and the close icon cancels. Names are normalized and trimmed, allow 1–128 Unicode characters, and reject control/invisible diff --git a/frontend/src/i18n/resources/en-US/migrations.json b/frontend/src/i18n/resources/en-US/migrations.json index 1671575ab..2c86b0279 100644 --- a/frontend/src/i18n/resources/en-US/migrations.json +++ b/frontend/src/i18n/resources/en-US/migrations.json @@ -145,9 +145,11 @@ "reselect": "Select again", "selectZip": "Select ZIP", "continue": "Continue upload", - "start": "Start migration", + "start": "Upload and analyze", "inputAria": "Select a local project ZIP", - "retention": "The temporary migration environment is retained for 1 hour after creation. Successfully saved source versions are unaffected." + "retention": "The temporary migration environment is retained for 1 hour after creation. Successfully saved source versions are unaffected.", + "uploading": "Uploading…", + "sizeHint": "Project ZIP, up to {{size}}" }, "deployment": { "requiredPlaceholder": "Enter {{key}}", @@ -160,11 +162,21 @@ "title": "Migrate existing project", "newMigration": "New migration", "recent": "Recent migrations", - "sessionsAria": "Migration sessions", "loadingSessions": "Loading migration sessions…", "noSessions": "No migration sessions", "heading": "Migrate an existing Agent project", - "intro": "Upload a local project ZIP. Codex will first perform a read-only analysis, then ask you to confirm the migration method." + "intro": "Upload a local project ZIP. Codex will first perform a read-only analysis, then ask you to confirm the migration method.", + "backToHome": "Migration home", + "navigation": "Migration navigation", + "showMore": "Show more", + "showLess": "Show less", + "projectName": "Project name", + "status": "Status", + "createdAt": "Created", + "actions": "Actions", + "openTask": "Open migration {{name}}", + "continueTask": "Continue", + "viewTask": "View" }, "actions": { "stop": "Stop migration", @@ -178,8 +190,6 @@ "defaultReason": "Dev Sandbox is currently unavailable. Contact your administrator to check the configuration." }, "conversation": { - "requestZip": "Provide a local project ZIP. After upload, I’ll identify the framework, entry point, and migration boundaries, then ask you to confirm the migration method before making changes.", - "zipHint": "Only local ZIP files are supported, up to {{size}}. The migration environment is retained for 1 hour after creation.", "creatingSandbox": "Creating Dev Sandbox", "initializing": "Initializing the migration workspace and checking AgentKit CLI, Codex, and migration capabilities. The project will upload automatically when the environment is ready.", "elapsed": "Elapsed: {{duration}}", diff --git a/frontend/src/i18n/resources/zh-CN/migrations.json b/frontend/src/i18n/resources/zh-CN/migrations.json index 6ca4bd2ea..15e94a47d 100644 --- a/frontend/src/i18n/resources/zh-CN/migrations.json +++ b/frontend/src/i18n/resources/zh-CN/migrations.json @@ -145,9 +145,11 @@ "reselect": "重新选择", "selectZip": "选择 ZIP", "continue": "继续上传", - "start": "开始迁移", + "start": "上传并分析", "inputAria": "选择本地项目 ZIP", - "retention": "临时迁移环境从创建完成起保留 1 小时;保存成功的源码版本不受影响。" + "retention": "临时迁移环境从创建完成起保留 1 小时;保存成功的源码版本不受影响。", + "uploading": "上传中…", + "sizeHint": "支持项目 ZIP,最大 {{size}}" }, "deployment": { "requiredPlaceholder": "请输入 {{key}}", @@ -160,11 +162,21 @@ "title": "从存量迁移", "newMigration": "新建迁移", "recent": "最近迁移", - "sessionsAria": "迁移会话", "loadingSessions": "正在读取迁移会话…", "noSessions": "暂无迁移会话", "heading": "迁移存量 Agent 项目", - "intro": "上传本地项目 ZIP,Codex 将先进行只读分析,再由你确认迁移方式。" + "intro": "上传本地项目 ZIP,Codex 将先进行只读分析,再由你确认迁移方式。", + "backToHome": "迁移首页", + "navigation": "迁移导航", + "showMore": "查看更多", + "showLess": "收起", + "projectName": "项目名称", + "status": "状态", + "createdAt": "创建时间", + "actions": "操作", + "openTask": "查看迁移 {{name}}", + "continueTask": "继续", + "viewTask": "查看" }, "actions": { "stop": "终止迁移", @@ -178,8 +190,6 @@ "defaultReason": "Dev Sandbox 暂不可用,请联系管理员检查配置。" }, "conversation": { - "requestZip": "请提供本地项目 ZIP。上传后我会识别框架、入口和迁移边界,并在执行实际迁移前请你确认迁移方式。", - "zipHint": "仅支持本地 ZIP,最大 {{size}};迁移环境从创建起保留 1 小时。", "creatingSandbox": "正在创建 Dev Sandbox", "initializing": "正在初始化迁移工作目录,并检查 AgentKit CLI、Codex 和迁移能力。环境就绪后将自动上传项目。", "elapsed": "已等待 {{duration}}", diff --git a/frontend/src/migrations/MigratedProjectsPage.tsx b/frontend/src/migrations/MigratedProjectsPage.tsx index dbf2595b6..63933482c 100644 --- a/frontend/src/migrations/MigratedProjectsPage.tsx +++ b/frontend/src/migrations/MigratedProjectsPage.tsx @@ -43,12 +43,6 @@ export function MigratedProjectsPage({ return ( <>
-
-
-

{t("projects.title")}

-

{t("projects.description")}

-
-
header { +.migration-workspace__nav, +.migration-workspace__location, +.migration-workspace__nav nav { display: flex; align-items: center; gap: 8px; - padding: 0 4px; + min-width: 0; +} + +.migration-workspace__nav { + min-height: 54px; + justify-content: space-between; + padding: 8px 24px; + border-bottom: 1px solid hsl(var(--border)); + background: hsl(var(--panel)); } -.migration-history h1 { +.migration-workspace__nav h1 { margin: 0; font-size: 15px; font-weight: 600; - line-height: 1.4; - letter-spacing: 0; + overflow-wrap: anywhere; +} + +.migration-home { + min-height: 0; + overflow: auto; + padding: 32px 32px 48px; } +.migration-home__content { + width: min(100%, var(--migration-content-width)); + margin: 0 auto; +} + +.migration-home__heading { margin-bottom: 24px; } +.migration-home__heading h2 { margin: 0; font-size: 21px; font-weight: 600; } +.migration-home__heading p { margin: 8px 0 0; color: hsl(var(--muted-foreground)); } + +.migration-history { margin-top: 28px; } +.migration-history > header { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 12px; } +.migration-history h2 { margin: 0; font-size: 17px; font-weight: 600; } +.migration-history table { width: 100%; table-layout: fixed; border-spacing: 0; border: 1px solid hsl(var(--border)); border-radius: 12px; overflow: hidden; background: hsl(var(--panel)); } +.migration-history th, .migration-history td { padding: 14px 16px; text-align: left; font-size: 13px; font-weight: 400; } +.migration-history thead { background: hsl(var(--secondary)); color: hsl(var(--muted-foreground)); } +.migration-history thead th:first-child { width: 36%; } +.migration-history thead th:last-child { width: 108px; } +.migration-history tbody th > span { display: block; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; } +.migration-history tbody tr + tr > * { border-top: 1px solid hsl(var(--border)); } +.migration-history time { color: hsl(var(--muted-foreground)); overflow-wrap: anywhere; } +.migration-history td .migration-new-button { min-height: 28px; white-space: nowrap; } + .migration-icon-button { width: 32px; height: 32px; @@ -75,14 +102,14 @@ } .migration-new-button { - min-height: 38px; + min-height: 36px; display: flex; align-items: center; justify-content: flex-start; gap: 9px; - margin: 2px 0; + margin: 0; padding: 0 10px; - border: 1px solid transparent; + border: 1px solid hsl(var(--border)); border-radius: 8px; background: transparent; color: hsl(var(--foreground)); @@ -98,79 +125,17 @@ border-color: hsl(var(--border) / 0.72); } -.migration-new-button.is-active, -.migration-new-button[aria-current="page"] { - border-color: hsl(var(--border) / 0.72); - background: hsl(var(--secondary)); -} - -.migration-history__label { - margin: 10px 10px 6px; - color: hsl(var(--muted-foreground)); - font-size: 11px; - font-weight: 550; - letter-spacing: 0.02em; -} - -.migration-new-button:disabled, -.migration-history nav > button:disabled { +.migration-new-button:disabled { cursor: not-allowed; opacity: 0.5; } -.migration-history nav { - min-height: 0; - display: flex; - flex-direction: column; - gap: 2px; - overflow-y: auto; -} - -.migration-history nav > button { - width: 100%; - min-height: 58px; - display: grid; - align-content: center; - gap: 6px; - padding: 9px 10px; - border: 0; - border-radius: 7px; - background: transparent; - color: hsl(var(--foreground)); - font: inherit; - text-align: left; - cursor: pointer; - transition: background-color 140ms ease, color 140ms ease; -} - -.migration-history nav > button:hover:not(:disabled), -.migration-history nav > button.is-active { - background: hsl(var(--secondary)); -} - -.migration-history nav > button > span { - overflow: hidden; - font-size: 13px; - font-weight: 500; - text-overflow: ellipsis; - white-space: nowrap; -} - -.migration-history nav small { - display: flex; - align-items: center; - justify-content: space-between; - gap: 6px; - color: hsl(var(--muted-foreground)); - font-size: 11px; - line-height: 1.35; -} - .migration-history__status { min-width: 0; display: flex; align-items: center; gap: 5px; + flex-wrap: wrap; } .migration-history__status-label { @@ -208,15 +173,10 @@ white-space: nowrap; } -.migration-history nav time { - flex-shrink: 0; -} - .migration-history__empty { - margin: 16px 8px; + margin: 12px 2px; color: hsl(var(--muted-foreground)); - font-size: 12px; - text-align: center; + font-size: 13px; } .migration-main { @@ -225,7 +185,7 @@ display: grid; grid-template-rows: auto minmax(0, 1fr) auto; overflow: hidden; - background: hsl(var(--background)); + background: hsl(var(--canvas)); } .migration-main__top { @@ -233,10 +193,11 @@ background: hsl(var(--panel)); } +.migration-projects-page { display: block; overflow-y: auto; } + .migration-projects-page__content { min-height: 0; - overflow-y: auto; - padding: 28px clamp(24px, 4vw, 56px) 48px; + padding: 28px 32px 48px; } .migration-projects-page__content > .ic-projects-panel { @@ -747,26 +708,96 @@ .migration-confirmation__grid { display: grid; - grid-template-columns: repeat(2, minmax(0, 1fr)); + grid-template-columns: minmax(0, 1fr); + gap: 12px; +} + +.migration-confirmation .migration-confirmation__row { + min-width: 0; + max-width: 640px; + display: grid; + grid-template-columns: 112px minmax(0, 1fr); + align-items: center; gap: 16px; } -.migration-confirmation .new-chat-compact-select { - align-self: end; +.migration-confirmation__row > span { + min-width: 0; + color: hsl(var(--foreground)); + font-size: 12.5px; + font-weight: 550; + overflow-wrap: anywhere; +} + +.migration-confirmation__row > span b { + margin-left: 3px; + color: hsl(var(--destructive)); +} + +.migration-confirmation__row > small { + grid-column: 2; +} + +.migration-confirmation__row > .new-chat-compact-select { + width: 100%; + max-width: none; } .migration-confirmation .new-chat-compact-select__trigger { width: 100%; min-height: 40px; + justify-content: space-between; + gap: 8px; + padding: 0 11px; border: 1px solid hsl(var(--border)); border-radius: 8px; background: hsl(var(--background)); + color: hsl(var(--foreground)); + font-size: 13.5px; +} + +.migration-confirmation .new-chat-compact-select__trigger:hover:not(:disabled), +.migration-confirmation .new-chat-compact-select__trigger[aria-expanded="true"] { + border-color: hsl(var(--ring) / 0.55); + background: hsl(var(--background)); +} + +.migration-confirmation .new-chat-compact-select__value { + font-size: 13.5px; + font-weight: 400; +} + +.migration-confirmation .new-chat-compact-select__chevron { + color: hsl(var(--muted-foreground)); } .migration-confirmation .new-chat-compact-select__menu { width: 100%; } +.migration-confirmation__footer { + display: flex; + align-items: center; + justify-content: space-between; + gap: 16px; + padding-top: 16px; + border-top: 1px solid hsl(var(--border)); +} + +.migration-confirmation__consent { + min-width: 0; + max-width: 640px; + margin: 0; + color: hsl(var(--muted-foreground)); + font-size: 12.5px; + font-weight: 400; + line-height: 1.55; +} + +.migration-confirmation__footer .migration-confirmation__actions { + flex: 0 0 auto; +} + .migration-field { min-width: 0; display: grid; @@ -1203,16 +1234,13 @@ min-height: 104px; position: relative; display: grid; - grid-template-rows: minmax(40px, auto) 36px; + grid-template-rows: auto auto; gap: 10px; box-sizing: border-box; padding: 12px 14px; border: 1px solid hsl(var(--foreground) / 0.14); - border-radius: 16px; + border-radius: 12px; background: hsl(var(--panel)); - box-shadow: - 0 8px 32px hsl(var(--foreground) / 0.028), - 0 24px 72px 8px hsl(var(--foreground) / 0.02); transition: background-color 140ms ease, border-color 140ms ease, box-shadow 140ms ease; } @@ -1436,11 +1464,37 @@ outline-offset: 2px; } -@media (max-width: 1120px) { - .migration-workspace { - grid-template-columns: 210px minmax(0, 1fr); - } +.migration-home .migration-composer { width: 100%; padding-bottom: 0; } +.migration-home .migration-composer__box { padding: 20px; gap: 16px; } +.migration-home .migration-composer__content { min-height: 144px; justify-content: center; border: 1px dashed hsl(var(--border)); border-radius: 8px; padding: 16px; } +.migration-upload-prompt { display: grid; justify-items: center; gap: 10px; text-align: center; } +.migration-upload-prompt > svg { width: 28px; height: 28px; color: hsl(var(--muted-foreground)); } +.migration-upload-prompt p { margin: 0; font-size: 15px; } +.migration-upload-prompt small { color: hsl(var(--muted-foreground)); font-size: 12px; } +.migration-composer__actions, .migration-composer__tools { gap: 12px; flex-wrap: wrap; } +.migration-composer__file { min-width: 0; } +.migration-composer__file span { min-width: 0; } +.migration-home .migration-composer > p { text-align: left; font-size: 12px; } + +@media (max-width: 640px) { + .migration-workspace__nav { padding: 8px 12px; flex-wrap: wrap; } + .migration-workspace__nav nav { flex-wrap: wrap; } + .migration-home, .migration-projects-page__content { padding: 24px 16px; } + .migration-home .migration-composer__box { padding: 12px; } + .migration-history th, .migration-history td { padding: 10px 6px; font-size: 12px; } + .migration-history thead th:first-child { width: 30%; } + .migration-history thead th:last-child { width: 76px; } + .migration-history td .migration-new-button { padding: 0 8px; } + .migration-history__status-label { white-space: normal; overflow-wrap: anywhere; } + .migration-main__header, .migration-confirmation__heading { align-items: flex-start; flex-direction: column; } + .migration-confirmation .migration-confirmation__row { grid-template-columns: minmax(0, 1fr); gap: 6px; } + .migration-confirmation__row > small { grid-column: 1; } + .migration-confirmation__footer { flex-direction: column; align-items: flex-start; } + .migration-main__header-actions { flex-wrap: wrap; } + .migration-composer { width: calc(100% - 32px); } +} +@media (max-width: 1120px) { .migration-main__header { padding-inline: 18px; } @@ -1453,7 +1507,6 @@ padding-inline: 20px; } - .migration-confirmation__grid, .migration-analysis__facts { grid-template-columns: minmax(0, 1fr); } diff --git a/frontend/src/migrations/MigrationWorkspace.tsx b/frontend/src/migrations/MigrationWorkspace.tsx index 3e8a659af..b9414a1c2 100644 --- a/frontend/src/migrations/MigrationWorkspace.tsx +++ b/frontend/src/migrations/MigrationWorkspace.tsx @@ -752,6 +752,10 @@ export function MigrationWorkspace({ const [modelsReloadKey, setModelsReloadKey] = useState(0); const [dragging, setDragging] = useState(false); const [loading, setLoading] = useState(true); + const [loadError, setLoadError] = useState(""); + const [capabilityError, setCapabilityError] = useState(""); + const [loadKey, setLoadKey] = useState(0); + const [showAllTasks, setShowAllTasks] = useState(false); const [action, setAction] = useState< "create" | "upload" | "answer" | "confirm" | "stop" | "download" | "" >(""); @@ -939,26 +943,38 @@ export function MigrationWorkspace({ useEffect(() => { const controller = new AbortController(); setLoading(true); - setError(""); - void Promise.all([ + setLoadError(""); + setCapabilityError(""); + void Promise.allSettled([ getMigrationCapabilities(controller.signal), listMigrationTasks(controller.signal), ]) - .then(([nextCapability, nextTasks]) => { + .then(([capabilityResult, taskResult]) => { if (controller.signal.aborted) return; - setCapability(nextCapability); - setTasks(nextTasks); - }) - .catch((cause: unknown) => { - if (!controller.signal.aborted) { - setError(cause instanceof Error ? cause.message : String(cause)); + if (capabilityResult.status === "fulfilled") { + setCapability(capabilityResult.value); + } else { + setCapabilityError( + capabilityResult.reason instanceof Error + ? capabilityResult.reason.message + : String(capabilityResult.reason), + ); + } + if (taskResult.status === "fulfilled") { + setTasks(taskResult.value); + } else { + setLoadError( + taskResult.reason instanceof Error + ? taskResult.reason.message + : String(taskResult.reason), + ); } }) .finally(() => { if (!controller.signal.aborted) setLoading(false); }); return () => controller.abort(); - }, []); + }, [loadKey]); useEffect(() => { const controller = new AbortController(); @@ -1955,6 +1971,8 @@ export function MigrationWorkspace({ const composerFile = sourceFile; const composerBusy = action === "create" || action === "upload"; + const isHome = page === "new" && !task && action !== "create"; + const navigationBusy = composerBusy || action === "confirm" || action === "answer" || action === "stop" || Boolean(evaluationAction); const showComposer = !task || (task.canUpload && !taskEnvironmentExpired); const expiryCopy = task ? migrationExpiryCopy(task, now) : null; const hasEvaluationTab = Boolean(task?.evaluation?.enabled); @@ -1978,103 +1996,152 @@ export function MigrationWorkspace({ ? evaluationDraftLoadError : null; + const composer = activeTaskTab === "migration" && showComposer && capability?.enabled ? ( +
+
{ + event.preventDefault(); + if (composerBusy) return; + setDragging(true); + }} + onDragOver={(event) => { + event.preventDefault(); + event.dataTransfer.dropEffect = composerBusy ? "none" : "copy"; + }} + onDragLeave={(event: DragEvent) => { + if ( + !event.currentTarget.contains( + event.relatedTarget as Node | null, + ) + ) { + setDragging(false); + } + }} + onDrop={(event) => { + event.preventDefault(); + setDragging(false); + if (composerBusy) return; + selectFile(event.dataTransfer.files?.[0]); + }} + > +
+ {composerFile ? ( +
+ + {composerFile.name} + {formatBytes(composerFile.size)} + +
+ ) : ( +
+ +

{task ? t("upload.reselectPrompt") : t("upload.selectPrompt")}

+ {t("upload.sizeHint", { size: maxSourceSizeLabel })} +
+ )} +
+
+
+ +
+ + setModelsReloadKey((current) => current + 1) + } + /> +
+
+ +
+ { + setEvaluationDraft(value); + setEvaluationErrors({}); + }} + capability={capability.evaluation} + disabled={composerBusy} + configLocked={Boolean(task)} + locked={Boolean(task?.evaluation?.dataset)} + errors={evaluationErrors} + /> + +
+

+ {t("upload.retention")} +

+
+ ) : null; + return ( <>
- + {page === "projects" ? ( + ) : isHome ? ( +
+
+
+

{t("workspace.heading")}

+

{t("workspace.intro")}

+
+ {loading ? {t("workspace.loadingSessions")} : null} + {loadError ? ( +
+ {localeCompatibleBackendText(loadError, locale) || t("errors.loadFailed")} + +
+ ) : null} + {capabilityError ? ( +
+ {localeCompatibleBackendText(capabilityError, locale) || t("errors.loadFailed")} + +
+ ) : null} + {capability && !capability.enabled ? ( +
+ {t("capability.unavailable")} +

{localeCompatibleBackendText(capability.reason, locale) || t("capability.defaultReason")}

+
+ ) : null} + {composer} + {error ? ( +
+ {localeCompatibleBackendText(error, locale) || t("errors.loadFailed")} + +
+ ) : null} +
+
+

{t("workspace.recent")}

+ {tasks.length > 5 ? ( + + ) : null} +
+ {pollError ? ( +
+ {localeCompatibleBackendText(pollError, locale) || t("errors.refreshFailed")} + {pollErrorRetryable ? : null} +
+ ) : null} + {!loading && !loadError && tasks.length === 0 ?

{t("workspace.noSessions")}

: null} + {tasks.length > 0 ? ( + + + + + + + + {(showAllTasks ? tasks : tasks.slice(0, 5)).map((item) => { + const status = migrationHistoryStatus(item); + const environmentExpired = isMigrationEnvironmentExpired(item, now); + const statusLabel = migrationText(status.labelKey); + return ( + + + + + + + ); + })} +
{t("workspace.projectName")}{t("workspace.status")}{t("workspace.createdAt")}{t("workspace.actions")}
{sourceStem(item.sourceFileName)} + {statusLabel} + {environmentExpired ? {t("historyStatus.environmentExpired")} : null} +
+ ) : null} +
+
+
) : (
@@ -2195,6 +2350,7 @@ export function MigrationWorkspace({ {t("capability.unavailable")}

{localeCompatibleBackendText(capability?.reason, locale) + || localeCompatibleBackendText(capabilityError, locale) || t("capability.defaultReason")}

@@ -2202,13 +2358,6 @@ export function MigrationWorkspace({ {!task ? ( <> -
-
AI
-
-

{t("conversation.requestZip")}

- {t("conversation.zipHint", { size: maxSourceSizeLabel })} -
-
{action === "create" && sourceFile ? ( <>
@@ -2401,25 +2550,29 @@ export function MigrationWorkspace({ {t("confirmation.description")}
- ({ - value: item, - label: frameworkLabel(item), - }))} - onChange={(value) => { - const next = value as MigrationFramework; - setFramework(next); - const candidate = task.analysis?.entries.find( - (item) => item.framework === next, - ); - setEntry(candidate?.value || ""); - }} - placeholder={t("confirmation.frameworkPlaceholder")} - disabled={Boolean(action)} - /> -
-

- {t("confirmation.consent")} -

-
- +
+

+ {t("confirmation.consent")} +

+
+ +
) : null} @@ -2708,120 +2867,7 @@ export function MigrationWorkspace({ ) : null}
- {activeTaskTab === "migration" && showComposer && capability?.enabled ? ( -
-
{ - event.preventDefault(); - if (composerBusy) return; - setDragging(true); - }} - onDragOver={(event) => { - event.preventDefault(); - event.dataTransfer.dropEffect = composerBusy ? "none" : "copy"; - }} - onDragLeave={(event: DragEvent) => { - if ( - !event.currentTarget.contains( - event.relatedTarget as Node | null, - ) - ) { - setDragging(false); - } - }} - onDrop={(event) => { - event.preventDefault(); - setDragging(false); - if (composerBusy) return; - selectFile(event.dataTransfer.files?.[0]); - }} - > -
- {composerFile ? ( -
- - {composerFile.name} - {formatBytes(composerFile.size)} - -
- ) : ( -

{task ? t("upload.reselectPrompt") : t("upload.selectPrompt")}

- )} -
-
-
- -
- - setModelsReloadKey((current) => current + 1) - } - /> -
-
- -
- { - setEvaluationDraft(value); - setEvaluationErrors({}); - }} - capability={capability.evaluation} - disabled={composerBusy} - configLocked={Boolean(task)} - locked={Boolean(task?.evaluation?.dataset)} - errors={evaluationErrors} - /> - -
-

- {t("upload.retention")} -

-
- ) : null} + {composer}
)} diff --git a/frontend/tests/migrationWorkspace.test.mjs b/frontend/tests/migrationWorkspace.test.mjs index 5a72bd5c8..f095df45d 100644 --- a/frontend/tests/migrationWorkspace.test.mjs +++ b/frontend/tests/migrationWorkspace.test.mjs @@ -232,7 +232,7 @@ test("keeps new migration and migrated projects as parallel workspace pages", () assert.match(source, /t\("workspace\.recent"\)/); assert.match( source, - /aria-current=\{page === "projects" \? "page" : undefined\}/, + /className="migration-workspace__nav"[\s\S]*?t\("workspace\.backToHome"\)[\s\S]*?t\("workspace\.newMigration"\)/, ); assert.match(source, /page === "projects" \? \(/); assert.match(source, / { ); assert.doesNotMatch(pollingEffect, /setActivity\(null\)/); }); + +test("migration home exposes upload and recent tasks without a navigation sidebar", () => { + const source = readFileSync(workspaceUrl, "utf8"); + const styles = readFileSync(stylesUrl, "utf8"); + assert.doesNotMatch(source, /