Skip to content

Commit 3d6f1b0

Browse files
alari76claude
andauthored
fix: improve Edit Workflow modal layout with two-column design (#393)
The modal was too narrow (480px) forcing excessive vertical scrolling. Widen to 720px and use a side-by-side grid layout: workflow type selection on the left, schedule + provider config on the right. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 02b2e38 commit 3d6f1b0

File tree

1 file changed

+97
-91
lines changed

1 file changed

+97
-91
lines changed

src/components/EditWorkflowModal.tsx

Lines changed: 97 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ export function EditWorkflowModal({ token, repo, onClose, onSave }: Props) {
8282
return (
8383
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/60" onClick={onClose}>
8484
<div
85-
className="w-[480px] max-h-[90vh] overflow-y-auto rounded-xl border border-neutral-7 bg-neutral-11 p-6 shadow-2xl"
85+
className="w-[720px] max-h-[90vh] overflow-y-auto rounded-xl border border-neutral-7 bg-neutral-11 p-6 shadow-2xl"
8686
onClick={e => e.stopPropagation()}
8787
>
8888
{/* Header */}
@@ -101,115 +101,121 @@ export function EditWorkflowModal({ token, repo, onClose, onSave }: Props) {
101101
</div>
102102

103103
<form onSubmit={handleSubmit} className="space-y-5">
104-
{/* Workflow kind */}
105-
<div>
106-
<div className="flex items-center justify-between mb-2">
107-
<label className="text-[13px] font-medium text-neutral-3">Workflow type</label>
108-
<CategoryBadge kind={form.kind} />
109-
</div>
110-
<div className="grid grid-cols-2 gap-2">
111-
{WORKFLOW_KINDS.map(k => (
112-
<button
113-
key={k.value}
114-
type="button"
115-
onClick={() => setForm(f => ({ ...f, kind: k.value }))}
116-
className={`rounded-lg border px-3 py-2.5 text-left transition-colors ${
117-
form.kind === k.value
118-
? 'border-accent-6 bg-accent-9/30 ring-1 ring-accent-6/30'
119-
: 'border-neutral-7 bg-neutral-10 hover:border-neutral-6'
120-
}`}
121-
>
122-
<span className={`block text-[15px] font-medium ${
123-
form.kind === k.value ? 'text-accent-2' : 'text-neutral-2'
124-
}`}>
125-
{k.label}
126-
</span>
127-
</button>
128-
))}
129-
</div>
130-
</div>
131-
132-
{/* Schedule — hidden for event-driven workflows */}
133-
{eventDriven ? (
134-
<div className="rounded-lg border border-purple-700/40 bg-purple-900/20 px-4 py-3">
135-
<span className="text-[14px] font-medium text-purple-400">
136-
{form.kind === 'pr-review' ? 'Trigger: On pull request' : 'Trigger: On commit'}
137-
</span>
138-
<p className="text-[13px] text-neutral-4 mt-1">
139-
{form.kind === 'pr-review'
140-
? 'Each PR will be reviewed automatically when opened, updated, or reopened. No schedule needed.'
141-
: 'Each commit will be reviewed automatically. No schedule needed.'}
142-
</p>
143-
</div>
144-
) : (
104+
{/* Two-column layout: Workflow type | Schedule + Provider */}
105+
<div className="grid grid-cols-2 gap-5">
106+
{/* Left column — Workflow kind */}
145107
<div>
146-
<label className="block text-[13px] font-medium text-neutral-3 mb-2">Time</label>
147-
<TimePicker
148-
hour={form.cronHour}
149-
minute={form.cronMinute}
150-
onChange={(h, m) => setForm(f => ({ ...f, cronHour: h, cronMinute: m }))}
151-
className="mb-3"
152-
/>
153-
<label className="block text-[13px] font-medium text-neutral-3 mb-2">Frequency</label>
154-
<div className="flex flex-wrap gap-1.5 mb-2">
155-
{DAY_PRESETS.map(p => (
156-
<button
157-
key={p.dow}
158-
type="button"
159-
onClick={() => setForm(f => ({ ...f, cronDow: p.dow }))}
160-
className={btnClass(form.cronDow === p.dow)}
161-
>
162-
{p.label}
163-
</button>
164-
))}
108+
<div className="flex items-center justify-between mb-2">
109+
<label className="text-[13px] font-medium text-neutral-3">Workflow type</label>
110+
<CategoryBadge kind={form.kind} />
165111
</div>
166-
<div className="flex gap-1.5">
167-
{DAY_INDIVIDUAL.map(p => (
112+
<div className="grid grid-cols-2 gap-2">
113+
{WORKFLOW_KINDS.map(k => (
168114
<button
169-
key={p.dow}
115+
key={k.value}
170116
type="button"
171-
onClick={() => setForm(f => ({ ...f, cronDow: biweekly ? `biweekly-${p.dow}` : p.dow }))}
172-
className={btnClass(baseDow === p.dow)}
117+
onClick={() => setForm(f => ({ ...f, kind: k.value }))}
118+
className={`rounded-lg border px-3 py-2.5 text-left transition-colors ${
119+
form.kind === k.value
120+
? 'border-accent-6 bg-accent-9/30 ring-1 ring-accent-6/30'
121+
: 'border-neutral-7 bg-neutral-10 hover:border-neutral-6'
122+
}`}
173123
>
174-
{p.label}
124+
<span className={`block text-[13px] font-medium ${
125+
form.kind === k.value ? 'text-accent-2' : 'text-neutral-2'
126+
}`}>
127+
{k.label}
128+
</span>
175129
</button>
176130
))}
177131
</div>
178-
{isDay && (
179-
<div className="flex gap-1.5 mt-2">
180-
<button type="button" onClick={() => setForm(f => ({ ...f, cronDow: baseDow }))} className={btnClass(!biweekly)}>
181-
Every week
182-
</button>
183-
<button type="button" onClick={() => setForm(f => ({ ...f, cronDow: `biweekly-${baseDow}` }))} className={btnClass(biweekly)}>
184-
Every 2 weeks
185-
</button>
132+
</div>
133+
134+
{/* Right column — Schedule + Provider */}
135+
<div className="space-y-4">
136+
{/* Schedule — hidden for event-driven workflows */}
137+
{eventDriven ? (
138+
<div className="rounded-lg border border-purple-700/40 bg-purple-900/20 px-4 py-3">
139+
<span className="text-[14px] font-medium text-purple-400">
140+
{form.kind === 'pr-review' ? 'Trigger: On pull request' : 'Trigger: On commit'}
141+
</span>
142+
<p className="text-[13px] text-neutral-4 mt-1">
143+
{form.kind === 'pr-review'
144+
? 'Each PR will be reviewed automatically when opened, updated, or reopened. No schedule needed.'
145+
: 'Each commit will be reviewed automatically. No schedule needed.'}
146+
</p>
147+
</div>
148+
) : (
149+
<div>
150+
<label className="block text-[13px] font-medium text-neutral-3 mb-2">Time</label>
151+
<TimePicker
152+
hour={form.cronHour}
153+
minute={form.cronMinute}
154+
onChange={(h, m) => setForm(f => ({ ...f, cronHour: h, cronMinute: m }))}
155+
className="mb-3"
156+
/>
157+
<label className="block text-[13px] font-medium text-neutral-3 mb-2">Frequency</label>
158+
<div className="flex flex-wrap gap-1.5 mb-2">
159+
{DAY_PRESETS.map(p => (
160+
<button
161+
key={p.dow}
162+
type="button"
163+
onClick={() => setForm(f => ({ ...f, cronDow: p.dow }))}
164+
className={btnClass(form.cronDow === p.dow)}
165+
>
166+
{p.label}
167+
</button>
168+
))}
169+
</div>
170+
<div className="flex flex-wrap gap-1.5">
171+
{DAY_INDIVIDUAL.map(p => (
172+
<button
173+
key={p.dow}
174+
type="button"
175+
onClick={() => setForm(f => ({ ...f, cronDow: biweekly ? `biweekly-${p.dow}` : p.dow }))}
176+
className={btnClass(baseDow === p.dow)}
177+
>
178+
{p.label}
179+
</button>
180+
))}
181+
</div>
182+
{isDay && (
183+
<div className="flex gap-1.5 mt-2">
184+
<button type="button" onClick={() => setForm(f => ({ ...f, cronDow: baseDow }))} className={btnClass(!biweekly)}>
185+
Every week
186+
</button>
187+
<button type="button" onClick={() => setForm(f => ({ ...f, cronDow: `biweekly-${baseDow}` }))} className={btnClass(biweekly)}>
188+
Every 2 weeks
189+
</button>
190+
</div>
191+
)}
192+
<div className="mt-2 text-[13px] text-neutral-5">
193+
{describeCron(buildCron(form.cronHour, form.cronDow, form.cronMinute))}
194+
</div>
186195
</div>
187196
)}
188-
<div className="mt-2 text-[13px] text-neutral-5">
189-
{describeCron(buildCron(form.cronHour, form.cronDow, form.cronMinute))}
190-
</div>
191-
</div>
192-
)}
193197

194-
{/* Provider + Model selection */}
195-
<ProviderModelSection
196-
token={token}
197-
workingDir={repo.repoPath}
198-
provider={form.provider}
199-
model={form.model}
200-
onProviderChange={provider => setForm(f => ({ ...f, provider }))}
201-
onModelChange={model => setForm(f => ({ ...f, model }))}
202-
/>
198+
{/* Provider + Model selection */}
199+
<ProviderModelSection
200+
token={token}
201+
workingDir={repo.repoPath}
202+
provider={form.provider}
203+
model={form.model}
204+
onProviderChange={provider => setForm(f => ({ ...f, provider }))}
205+
onModelChange={model => setForm(f => ({ ...f, model }))}
206+
/>
207+
</div>
208+
</div>
203209

204-
{/* Custom prompt */}
210+
{/* Custom prompt — full width */}
205211
<div>
206212
<label className="block text-[13px] font-medium text-neutral-3 mb-1.5">
207213
Focus areas <span className="text-neutral-5 font-normal">(optional)</span>
208214
</label>
209215
<textarea
210216
value={form.customPrompt}
211217
onChange={e => setForm(f => ({ ...f, customPrompt: e.target.value }))}
212-
rows={3}
218+
rows={2}
213219
placeholder="e.g. Focus on the auth module and payment flows"
214220
className="w-full rounded-md border border-neutral-7 bg-neutral-10 px-3 py-2 text-[15px] text-neutral-1 placeholder-neutral-5 focus:border-accent-6 focus:outline-none resize-none"
215221
/>

0 commit comments

Comments
 (0)