mirror of
https://github.com/block/buzz.git
synced 2026-08-18 06:50:31 +02:00
Polish workflow enable controls
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
This commit is contained in:
@@ -30,11 +30,13 @@ import { rewriteRelayUrl } from "@/shared/lib/mediaUrl";
|
||||
import { Button } from "@/shared/ui/button";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuCheckboxItem,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger,
|
||||
} from "@/shared/ui/dropdown-menu";
|
||||
import { Switch } from "@/shared/ui/switch";
|
||||
import {
|
||||
getWorkflowCardLabel,
|
||||
getWorkflowDisplayStatus,
|
||||
@@ -377,17 +379,28 @@ export function WorkflowCard({
|
||||
<Copy className="mr-2 h-4 w-4" />
|
||||
Duplicate
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem
|
||||
<DropdownMenuCheckboxItem
|
||||
checked={isEnabled}
|
||||
className="gap-2 pl-2 [&>span:first-child]:hidden"
|
||||
disabled={isTogglingEnabled}
|
||||
onClick={() => onToggleEnabled(workflow)}
|
||||
onCheckedChange={(checked) => {
|
||||
if (checked !== isEnabled) onToggleEnabled(workflow);
|
||||
}}
|
||||
onSelect={(event) => event.preventDefault()}
|
||||
>
|
||||
{isEnabled ? (
|
||||
<PowerOff className="mr-2 h-4 w-4" />
|
||||
<Power className="mr-2 h-4 w-4 shrink-0" />
|
||||
) : (
|
||||
<Power className="mr-2 h-4 w-4" />
|
||||
<PowerOff className="mr-2 h-4 w-4 shrink-0" />
|
||||
)}
|
||||
{isEnabled ? "Disable" : "Enable"}
|
||||
</DropdownMenuItem>
|
||||
<span>Enable</span>
|
||||
<Switch
|
||||
aria-hidden="true"
|
||||
checked={isEnabled}
|
||||
className="pointer-events-none ml-auto"
|
||||
tabIndex={-1}
|
||||
/>
|
||||
</DropdownMenuCheckboxItem>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem
|
||||
className="text-destructive"
|
||||
|
||||
@@ -116,11 +116,13 @@ function WorkflowNameEditor({
|
||||
generating,
|
||||
name,
|
||||
onCommit,
|
||||
onEditingChange,
|
||||
}: {
|
||||
disabled: boolean;
|
||||
generating: boolean;
|
||||
name: string;
|
||||
onCommit: (name: string) => boolean;
|
||||
onEditingChange: (editing: boolean) => void;
|
||||
}) {
|
||||
const [editing, setEditing] = React.useState(false);
|
||||
const [draft, setDraft] = React.useState(name);
|
||||
@@ -134,11 +136,19 @@ function WorkflowNameEditor({
|
||||
if (editing) inputRef.current?.select();
|
||||
}, [editing]);
|
||||
|
||||
const changeEditing = React.useCallback(
|
||||
(nextEditing: boolean) => {
|
||||
setEditing(nextEditing);
|
||||
onEditingChange(nextEditing);
|
||||
},
|
||||
[onEditingChange],
|
||||
);
|
||||
|
||||
const commit = React.useCallback(() => {
|
||||
const nextName = draft.trim();
|
||||
if (!nextName || !onCommit(nextName)) return;
|
||||
setEditing(false);
|
||||
}, [draft, onCommit]);
|
||||
changeEditing(false);
|
||||
}, [changeEditing, draft, onCommit]);
|
||||
|
||||
if (editing) {
|
||||
return (
|
||||
@@ -155,7 +165,7 @@ function WorkflowNameEditor({
|
||||
} else if (event.key === "Escape") {
|
||||
event.preventDefault();
|
||||
setDraft(name);
|
||||
setEditing(false);
|
||||
changeEditing(false);
|
||||
}
|
||||
}}
|
||||
ref={inputRef}
|
||||
@@ -186,7 +196,7 @@ function WorkflowNameEditor({
|
||||
aria-label="Edit workflow name"
|
||||
className="text-muted-foreground [&_svg]:size-3"
|
||||
disabled={disabled || generating}
|
||||
onClick={() => setEditing(true)}
|
||||
onClick={() => changeEditing(true)}
|
||||
size="icon-xs"
|
||||
title="Edit workflow name"
|
||||
type="button"
|
||||
@@ -220,7 +230,8 @@ export function WorkflowDialog({
|
||||
const [editorParseError, setEditorParseError] = React.useState<string | null>(
|
||||
null,
|
||||
);
|
||||
const [headerTrailingElement, setHeaderTrailingElement] =
|
||||
const [workflowNameEditing, setWorkflowNameEditing] = React.useState(false);
|
||||
const [nameLeadingElement, setNameLeadingElement] =
|
||||
React.useState<HTMLDivElement | null>(null);
|
||||
const [savedWebhookInfo, setSavedWebhookInfo] = React.useState<{
|
||||
relayHttpUrl: string;
|
||||
@@ -265,6 +276,7 @@ export function WorkflowDialog({
|
||||
setYamlDefinition(initialYaml);
|
||||
setEditorMode(getInitialEditorMode(initialYaml));
|
||||
setEditorParseError(null);
|
||||
setWorkflowNameEditing(false);
|
||||
setSavedWebhookInfo(null);
|
||||
setDiscardConfirmationOpen(false);
|
||||
resetCreate();
|
||||
@@ -431,15 +443,25 @@ export function WorkflowDialog({
|
||||
? "Copy this workflow and adjust its details."
|
||||
: "Automate actions when something happens in a channel."}
|
||||
</DialogDescription>
|
||||
<WorkflowNameEditor
|
||||
disabled={mutation.isPending || !canEditWorkflowName}
|
||||
generating={generatingName}
|
||||
name={workflowName}
|
||||
onCommit={handleWorkflowNameCommit}
|
||||
/>
|
||||
<div className="flex items-center gap-2">
|
||||
<WorkflowNameEditor
|
||||
disabled={mutation.isPending || !canEditWorkflowName}
|
||||
generating={generatingName}
|
||||
name={workflowName}
|
||||
onCommit={handleWorkflowNameCommit}
|
||||
onEditingChange={setWorkflowNameEditing}
|
||||
/>
|
||||
<div
|
||||
className={
|
||||
editorMode === "form" && !workflowNameEditing
|
||||
? "flex items-center"
|
||||
: "hidden"
|
||||
}
|
||||
ref={setNameLeadingElement}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<div ref={setHeaderTrailingElement} />
|
||||
<DialogClose asChild>
|
||||
<Button
|
||||
aria-label="Close"
|
||||
@@ -458,8 +480,8 @@ export function WorkflowDialog({
|
||||
<WorkflowFormBuilder
|
||||
channels={channels}
|
||||
disabled={mutation.isPending}
|
||||
headerTrailingContainer={headerTrailingElement}
|
||||
mode={editorMode}
|
||||
nameLeadingContainer={nameLeadingElement}
|
||||
onChange={(yaml) => {
|
||||
mutation.reset();
|
||||
yamlDefinitionRef.current = yaml;
|
||||
|
||||
@@ -147,8 +147,8 @@ function TriggerConfigFields({
|
||||
type WorkflowFormBuilderProps = {
|
||||
channels: Channel[];
|
||||
disabled?: boolean;
|
||||
headerTrailingContainer?: HTMLElement | null;
|
||||
mode: WorkflowEditorMode;
|
||||
nameLeadingContainer?: HTMLElement | null;
|
||||
onChange: (yaml: string) => void;
|
||||
onSelectedNodeChange: (node: WorkflowEditorPane) => void;
|
||||
parseError: string | null;
|
||||
@@ -365,8 +365,8 @@ function WorkflowNode({
|
||||
export function WorkflowFormBuilder({
|
||||
channels,
|
||||
disabled,
|
||||
headerTrailingContainer,
|
||||
mode,
|
||||
nameLeadingContainer,
|
||||
onChange,
|
||||
onSelectedNodeChange,
|
||||
parseError,
|
||||
@@ -822,22 +822,18 @@ export function WorkflowFormBuilder({
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{mode === "form" && headerTrailingContainer
|
||||
{mode === "form" && nameLeadingContainer
|
||||
? createPortal(
|
||||
<div className="flex items-center gap-3">
|
||||
<label className="text-sm text-foreground" htmlFor="wf-enabled">
|
||||
Enable
|
||||
</label>
|
||||
<Switch
|
||||
checked={formState.enabled}
|
||||
disabled={disabled}
|
||||
id="wf-enabled"
|
||||
onCheckedChange={(checked) =>
|
||||
updateFormState({ ...formState, enabled: checked })
|
||||
}
|
||||
/>
|
||||
</div>,
|
||||
headerTrailingContainer,
|
||||
<Switch
|
||||
aria-label="Enable workflow"
|
||||
checked={formState.enabled}
|
||||
disabled={disabled}
|
||||
id="wf-enabled"
|
||||
onCheckedChange={(checked) =>
|
||||
updateFormState({ ...formState, enabled: checked })
|
||||
}
|
||||
/>,
|
||||
nameLeadingContainer,
|
||||
)
|
||||
: null}
|
||||
</>
|
||||
|
||||
Reference in New Issue
Block a user