CreateWorkspaceModal.svelte
1 <script lang="ts"> 2 import { Button, Modal, Input } from '../common'; 3 import type { ParentWorkspace } from '../../lib/types'; 4 5 interface Props { 6 open: boolean; 7 type: 'parent' | 'child' | 'standalone'; 8 parentWorkspaces?: ParentWorkspace[]; 9 selectedParentId?: string; 10 oncreate?: (data: { name: string; type: string; parentId?: string }) => void; 11 onclose?: () => void; 12 } 13 14 let { 15 open = $bindable(), 16 type = 'standalone', 17 parentWorkspaces = [], 18 selectedParentId, 19 oncreate, 20 onclose, 21 }: Props = $props(); 22 23 let name = $state(''); 24 let parentId = $state(''); 25 let error = $state(''); 26 let creating = $state(false); 27 const selectId = 'parent-workspace-select'; 28 29 // Reset form when modal opens 30 $effect(() => { 31 if (open) { 32 name = ''; 33 // Use the prop or first available parent 34 parentId = selectedParentId || (parentWorkspaces[0]?.id ?? ''); 35 error = ''; 36 creating = false; 37 } 38 }); 39 40 const title = $derived( 41 type === 'parent' ? 'Create Parent Workspace' : 42 type === 'child' ? 'Create Child Workspace' : 43 'Create Standalone Workspace' 44 ); 45 46 async function handleSubmit() { 47 if (!name.trim()) { 48 error = 'Name is required'; 49 return; 50 } 51 52 if (type === 'child' && !parentId) { 53 error = 'Please select a parent workspace'; 54 return; 55 } 56 57 creating = true; 58 error = ''; 59 60 try { 61 oncreate?.({ 62 name: name.trim(), 63 type, 64 parentId: type === 'child' ? parentId : undefined, 65 }); 66 open = false; 67 } catch (e) { 68 error = e instanceof Error ? e.message : 'Failed to create workspace'; 69 } finally { 70 creating = false; 71 } 72 } 73 74 function handleClose() { 75 open = false; 76 onclose?.(); 77 } 78 </script> 79 80 <Modal bind:open {title} onclose={handleClose}> 81 <form onsubmit={(e) => { e.preventDefault(); handleSubmit(); }} class="space-y-4"> 82 <Input 83 bind:value={name} 84 label="Workspace Name" 85 placeholder="Enter workspace name" 86 error={error && !name.trim() ? error : undefined} 87 /> 88 89 {#if type === 'child'} 90 <div> 91 <label for={selectId} class="block text-sm font-mono text-text-secondary mb-1.5"> 92 Parent Workspace 93 </label> 94 <select 95 id={selectId} 96 bind:value={parentId} 97 class="w-full bg-mnemonic-bg-dark border border-phosphor-dark/50 rounded px-3 py-2 text-text-primary font-mono text-sm focus:outline-none focus:border-phosphor focus:ring-1 focus:ring-phosphor" 98 > 99 {#each parentWorkspaces as parent (parent.id)} 100 <option value={parent.id}>{parent.name}</option> 101 {/each} 102 </select> 103 {#if parentWorkspaces.length === 0} 104 <p class="mt-1 text-xs text-status-warning"> 105 No parent workspaces available. Create a parent first. 106 </p> 107 {/if} 108 </div> 109 {/if} 110 111 {#if error && name.trim()} 112 <p class="text-sm text-status-error">{error}</p> 113 {/if} 114 </form> 115 116 {#snippet footer()} 117 <Button variant="ghost" onclick={handleClose}> 118 Cancel 119 </Button> 120 <Button 121 variant="primary" 122 loading={creating} 123 disabled={!name.trim() || (type === 'child' && !parentId)} 124 onclick={handleSubmit} 125 > 126 Create 127 </Button> 128 {/snippet} 129 </Modal>