History

Add `delete` option for project actions (#223)

Adds a delete button to the action edit dialog with a confirmation alert. Fixes pingdotgg/t3code#208.

Mubin Ansari committed 8 months ago

Commit 1c43bad1 parent 18b65902 files · +79 −1Browse files

Unified · Split

2 files
  1. apps/web/src/components/ChatView.tsx +34 −0
  2. apps/web/src/components/ProjectScriptsControl.tsx +45 −1
apps/web/src/components/ChatView.tsx+34−0
@@ -1621,6 +1621,36 @@ export default function ChatView({ threadId }: ChatViewProps) { · 1620 unmodified lines
16211621 },
16221622 [activeProject, persistProjectScripts],
16231623 );
1624+ const deleteProjectScript = useCallback(
1625+ async (scriptId: string) => {
1626+ if (!activeProject) return;
1627+ const nextScripts = activeProject.scripts.filter((script) => script.id !== scriptId);
1628+
1629+ const deletedName = activeProject.scripts.find((s) => s.id === scriptId)?.name;
1630+
1631+ try {
1632+ await persistProjectScripts({
1633+ projectId: activeProject.id,
1634+ projectCwd: activeProject.cwd,
1635+ previousScripts: activeProject.scripts,
1636+ nextScripts,
1637+ keybinding: null,
1638+ keybindingCommand: commandForProjectScript(scriptId),
1639+ });
1640+ toastManager.add({
1641+ type: "success",
1642+ title: `Deleted action "${deletedName ?? "Unknown"}"`,
1643+ });
1644+ } catch (error) {
1645+ toastManager.add({
1646+ type: "error",
1647+ title: "Could not delete action",
1648+ description: error instanceof Error ? error.message : "An unexpected error occurred.",
1649+ });
1650+ }
1651+ },
1652+ [activeProject, persistProjectScripts],
1653+ );
16241654
16251655 const handleRuntimeModeChange = useCallback(
16261656 (mode: RuntimeMode) => {
@@ -3428,6 +3458,7 @@ export default function ChatView({ threadId }: ChatViewProps) { · 1801 unmodified lines
34283458 }}
34293459 onAddProjectScript={saveProjectScript}
34303460 onUpdateProjectScript={updateProjectScript}
3461+ onDeleteProjectScript={deleteProjectScript}
34313462 onToggleDiff={onToggleDiff}
34323463 />
34333464 </header>
@@ -4008,6 +4039,7 @@ interface ChatHeaderProps { · 574 unmodified lines
40084039 onRunProjectScript: (script: ProjectScript) => void;
40094040 onAddProjectScript: (input: NewProjectScriptInput) => Promise<void>;
40104041 onUpdateProjectScript: (scriptId: string, input: NewProjectScriptInput) => Promise<void>;
4042+ onDeleteProjectScript: (scriptId: string) => Promise<void>;
40114043 onToggleDiff: () => void;
40124044 }
40134045
@@ -4027,6 +4059,7 @@ const ChatHeader = memo(function ChatHeader({ · 13 unmodified lines
40274059 onRunProjectScript,
40284060 onAddProjectScript,
40294061 onUpdateProjectScript,
4062+ onDeleteProjectScript,
40304063 onToggleDiff,
40314064 }: ChatHeaderProps) {
40324065 return (
@@ -4059,6 +4092,7 @@ const ChatHeader = memo(function ChatHeader({ · 26 unmodified lines
40594092 onRunScript={onRunProjectScript}
40604093 onAddScript={onAddProjectScript}
40614094 onUpdateScript={onUpdateProjectScript}
4095+ onDeleteScript={onDeleteProjectScript}
40624096 />
40634097 )}
40644098 {activeProjectName && (
apps/web/src/components/ProjectScriptsControl.tsx+45−1
@@ -14,7 +14,7 @@ import { · 13 unmodified lines
1414 SettingsIcon,
1515 WrenchIcon,
1616 } from "lucide-react";
17−import React, { type FormEvent, type KeyboardEvent, useMemo, useState } from "react";
17+import React, { type FormEvent, type KeyboardEvent, useCallback, useMemo, useState } from "react";
1818
1919 import {
2020 keybindingValueForCommand,
@@ -27,6 +27,15 @@ import { · 6 unmodified lines
2727 } from "~/projectScripts";
2828 import { shortcutLabelForCommand } from "~/keybindings";
2929 import { isMacPlatform } from "~/lib/utils";
30+import {
31+ AlertDialog,
32+ AlertDialogClose,
33+ AlertDialogDescription,
34+ AlertDialogFooter,
35+ AlertDialogHeader,
36+ AlertDialogPopup,
37+ AlertDialogTitle,
38+} from "./ui/alert-dialog";
3039 import { Button } from "./ui/button";
3140 import {
3241 Dialog,
@@ -84,6 +93,7 @@ interface ProjectScriptsControlProps { · 51 unmodified lines
8493 onRunScript: (script: ProjectScript) => void;
8594 onAddScript: (input: NewProjectScriptInput) => Promise<void> | void;
8695 onUpdateScript: (scriptId: string, input: NewProjectScriptInput) => Promise<void> | void;
96+ onDeleteScript: (scriptId: string) => Promise<void> | void;
8797 }
8898
8999 function normalizeShortcutKeyToken(key: string): string | null {
@@ -144,6 +154,7 @@ export default function ProjectScriptsControl({ · 54 unmodified lines
144154 onRunScript,
145155 onAddScript,
146156 onUpdateScript,
157+ onDeleteScript,
147158 }: ProjectScriptsControlProps) {
148159 const addScriptFormId = React.useId();
149160 const [editingScriptId, setEditingScriptId] = useState<string | null>(null);
@@ -155,6 +166,7 @@ export default function ProjectScriptsControl({ · 5 unmodified lines
155166 const [runOnWorktreeCreate, setRunOnWorktreeCreate] = useState(false);
156167 const [keybinding, setKeybinding] = useState("");
157168 const [validationError, setValidationError] = useState<string | null>(null);
169+ const [deleteConfirmOpen, setDeleteConfirmOpen] = useState(false);
158170
159171 const primaryScript = useMemo(() => {
160172 if (preferredScriptId) {
@@ -247,6 +259,13 @@ export default function ProjectScriptsControl({ · 86 unmodified lines
247259 setDialogOpen(true);
248260 };
249261
262+ const confirmDeleteScript = useCallback(() => {
263+ if (!editingScriptId) return;
264+ setDeleteConfirmOpen(false);
265+ setDialogOpen(false);
266+ void onDeleteScript(editingScriptId);
267+ }, [editingScriptId, onDeleteScript]);
268+
250269 return (
251270 <>
252271 {primaryScript ? (
@@ -440,6 +459,16 @@ export default function ProjectScriptsControl({ · 187 unmodified lines
440459 </form>
441460 </DialogPanel>
442461 <DialogFooter>
462+ {isEditing && (
463+ <Button
464+ type="button"
465+ variant="destructive-outline"
466+ className="mr-auto"
467+ onClick={() => setDeleteConfirmOpen(true)}
468+ >
469+ Delete
470+ </Button>
471+ )}
443472 <Button
444473 type="button"
445474 variant="outline"
@@ -455,6 +484,21 @@ export default function ProjectScriptsControl({ · 9 unmodified lines
455484 </DialogFooter>
456485 </DialogPopup>
457486 </Dialog>
487+
488+ <AlertDialog open={deleteConfirmOpen} onOpenChange={setDeleteConfirmOpen}>
489+ <AlertDialogPopup>
490+ <AlertDialogHeader>
491+ <AlertDialogTitle>Delete action "{name}"?</AlertDialogTitle>
492+ <AlertDialogDescription>This action cannot be undone.</AlertDialogDescription>
493+ </AlertDialogHeader>
494+ <AlertDialogFooter>
495+ <AlertDialogClose render={<Button variant="outline" />}>Cancel</AlertDialogClose>
496+ <Button variant="destructive" onClick={confirmDeleteScript}>
497+ Delete action
498+ </Button>
499+ </AlertDialogFooter>
500+ </AlertDialogPopup>
501+ </AlertDialog>
458502 </>
459503 );
460504 }