|
14 | 14 | import { stateModelWorkspaceDialog } from "./stateModelNavigation.js"; |
15 | 15 | import { objectSnapshot } from "./editorDirtyState.js"; |
16 | 16 | import { resultFileUrl, resultGroupDeleteUrl, resultListUrl } from "./resultApi.js"; |
| 17 | + import { |
| 18 | + defaultWorkspaceCreationDraft, |
| 19 | + workspaceCreationRequest, |
| 20 | + workspaceCreationValidation |
| 21 | + } from "./workspaceCreationModel.js"; |
17 | 22 |
|
18 | 23 | const STATE_MODEL_URL = "http://localhost:8090/models"; |
19 | 24 | const CLI_AGENT_SETTING_KEYS = { |
|
95 | 100 | message: "", |
96 | 101 | saveLabel: "Save" |
97 | 102 | }; |
| 103 | + let workspaceCreationDialogOpen = false; |
| 104 | + let workspaceCreationDraft = defaultWorkspaceCreationDraft([], ""); |
| 105 | + let workspaceCreationError = ""; |
98 | 106 | let savedTestSettingsContent = ""; |
99 | 107 | let savedCompositionPropertiesContent = ""; |
100 | 108 | let savedPoliciesPropertiesContent = ""; |
|
809 | 817 | }); |
810 | 818 | } |
811 | 819 |
|
| 820 | + async function openWorkspaceCreationDialog() { |
| 821 | + await guardApplicationTransition(async () => { |
| 822 | + workspaceCreationDraft = defaultWorkspaceCreationDraft(workspaces, selectedWorkspaceName); |
| 823 | + workspaceCreationError = ""; |
| 824 | + workspaceCreationDialogOpen = true; |
| 825 | + }); |
| 826 | + } |
| 827 | +
|
| 828 | + function closeWorkspaceCreationDialog() { |
| 829 | + if (saving) { |
| 830 | + return; |
| 831 | + } |
| 832 | +
|
| 833 | + workspaceCreationDialogOpen = false; |
| 834 | + workspaceCreationError = ""; |
| 835 | + } |
| 836 | +
|
| 837 | + function closeWorkspaceCreationDialogFromBackdrop(event) { |
| 838 | + if (event.target === event.currentTarget) { |
| 839 | + closeWorkspaceCreationDialog(); |
| 840 | + } |
| 841 | + } |
| 842 | +
|
| 843 | + async function createWorkspaceFromDialog() { |
| 844 | + const validation = workspaceCreationValidation(workspaceCreationDraft, workspaces); |
| 845 | + if (!validation.valid) { |
| 846 | + workspaceCreationError = validation.message; |
| 847 | + return; |
| 848 | + } |
| 849 | +
|
| 850 | + saving = true; |
| 851 | + workspaceCreationError = ""; |
| 852 | + message = ""; |
| 853 | +
|
| 854 | + try { |
| 855 | + const request = workspaceCreationRequest(workspaceCreationDraft); |
| 856 | + const createdWorkspace = await loadJson("/api/workspaces", { |
| 857 | + method: "POST", |
| 858 | + headers: { |
| 859 | + "Content-Type": "application/json" |
| 860 | + }, |
| 861 | + body: JSON.stringify(request) |
| 862 | + }); |
| 863 | + await refreshInitialData(); |
| 864 | + currentPage = "configuration"; |
| 865 | + await loadWorkspace(createdWorkspace?.name || request.name); |
| 866 | + workspaceCreationDialogOpen = false; |
| 867 | + showTemporaryMessage(`Workspace ${request.name} created.`); |
| 868 | + } catch (createError) { |
| 869 | + reportClientError("Unable to create workspace", createError); |
| 870 | + workspaceCreationError = createError.message || "Unable to create workspace."; |
| 871 | + } finally { |
| 872 | + saving = false; |
| 873 | + } |
| 874 | + } |
| 875 | +
|
812 | 876 | function openStateModelExternalTab(url = STATE_MODEL_URL) { |
813 | 877 | window.open(url, "_blank", "noopener,noreferrer"); |
814 | 878 | } |
|
2170 | 2234 | } |
2171 | 2235 |
|
2172 | 2236 | $: selectedWorkspaceSummary = workspaces.find((workspace) => workspace.name === selectedWorkspaceName) || null; |
| 2237 | + $: workspaceCreationValidationState = workspaceCreationValidation(workspaceCreationDraft, workspaces); |
2173 | 2238 |
|
2174 | 2239 | onMount(async () => { |
2175 | 2240 | startScriptlessPolling(); |
|
2196 | 2261 | <div class="page"> |
2197 | 2262 | <nav class="panel panel-wide page-nav"> |
2198 | 2263 | <div class="page-nav-workspace"> |
| 2264 | + <button type="button" class="secondary page-workspace-add" on:click={openWorkspaceCreationDialog}> |
| 2265 | + New |
| 2266 | + </button> |
2199 | 2267 | <select |
2200 | 2268 | id="page-workspace-select" |
2201 | 2269 | value={selectedWorkspaceName} |
|
2382 | 2450 | </div> |
2383 | 2451 | {/if} |
2384 | 2452 |
|
| 2453 | + {#if workspaceCreationDialogOpen} |
| 2454 | + <div class="composition-modal-backdrop" role="presentation" on:click={closeWorkspaceCreationDialogFromBackdrop}> |
| 2455 | + <div |
| 2456 | + class="composition-modal state-model-dialog workspace-creation-dialog" |
| 2457 | + role="dialog" |
| 2458 | + aria-modal="true" |
| 2459 | + aria-labelledby="workspace-creation-dialog-title" |
| 2460 | + > |
| 2461 | + <div class="composition-modal-header"> |
| 2462 | + <div> |
| 2463 | + <h2 id="workspace-creation-dialog-title">Create New Workspace</h2> |
| 2464 | + <p>Select an existing workspace as base. WebStudio clones its settings, composition, policies, and Java files.</p> |
| 2465 | + </div> |
| 2466 | + </div> |
| 2467 | + <div class="composition-modal-body workspace-creation-form"> |
| 2468 | + <label> |
| 2469 | + <span>Workspace name</span> |
| 2470 | + <input |
| 2471 | + type="text" |
| 2472 | + bind:value={workspaceCreationDraft.name} |
| 2473 | + placeholder="platform_application (e.g., webdriver_parabank)" |
| 2474 | + disabled={saving} |
| 2475 | + /> |
| 2476 | + </label> |
| 2477 | + <label> |
| 2478 | + <span>Base workspace</span> |
| 2479 | + <select bind:value={workspaceCreationDraft.baseWorkspace} disabled={saving}> |
| 2480 | + {#each workspaces as workspace} |
| 2481 | + <option value={workspace.name}>{workspace.name}</option> |
| 2482 | + {/each} |
| 2483 | + </select> |
| 2484 | + </label> |
| 2485 | + <label class="workspace-creation-checkbox"> |
| 2486 | + <input |
| 2487 | + type="checkbox" |
| 2488 | + bind:checked={workspaceCreationDraft.copyTestGoals} |
| 2489 | + disabled={saving} |
| 2490 | + /> |
| 2491 | + <span>Copy Test Goals from base workspace</span> |
| 2492 | + </label> |
| 2493 | + {#if workspaceCreationError || workspaceCreationValidationState.message} |
| 2494 | + <p class:settings-validation-invalid={workspaceCreationError || !workspaceCreationValidationState.valid}> |
| 2495 | + {workspaceCreationError || workspaceCreationValidationState.message} |
| 2496 | + </p> |
| 2497 | + {/if} |
| 2498 | + </div> |
| 2499 | + <div class="composition-modal-actions"> |
| 2500 | + <button |
| 2501 | + type="button" |
| 2502 | + on:click={createWorkspaceFromDialog} |
| 2503 | + disabled={saving || !workspaceCreationValidationState.valid} |
| 2504 | + > |
| 2505 | + Create |
| 2506 | + </button> |
| 2507 | + <button type="button" class="secondary" on:click={closeWorkspaceCreationDialog} disabled={saving}> |
| 2508 | + Discard |
| 2509 | + </button> |
| 2510 | + </div> |
| 2511 | + </div> |
| 2512 | + </div> |
| 2513 | + {/if} |
| 2514 | +
|
2385 | 2515 | {#if stateModelDialog.open} |
2386 | 2516 | <div class="composition-modal-backdrop" role="presentation" on:click={closeStateModelDialogFromBackdrop}> |
2387 | 2517 | <div |
|
0 commit comments