diff --git a/ui/src/components/MacroForm.tsx b/ui/src/components/MacroForm.tsx index 44c819d..06afd4e 100644 --- a/ui/src/components/MacroForm.tsx +++ b/ui/src/components/MacroForm.tsx @@ -9,7 +9,6 @@ import Fieldset from "@/components/Fieldset"; import { MacroStepCard } from "@/components/MacroStepCard"; import { DEFAULT_DELAY, MAX_STEPS_PER_MACRO, MAX_KEYS_PER_STEP } from "@/constants/macros"; import FieldLabel from "@/components/FieldLabel"; -import { ConfirmDialog } from "@/components/ConfirmDialog"; interface ValidationErrors { name?: string; @@ -26,11 +25,6 @@ interface MacroFormProps { onCancel: () => void; isSubmitting?: boolean; submitText?: string; - showCancelConfirm?: boolean; - onCancelConfirm?: () => void; - showDelete?: boolean; - onDelete?: () => void; - isDeleting?: boolean; } export function MacroForm({ @@ -39,17 +33,11 @@ export function MacroForm({ onCancel, isSubmitting = false, submitText = "Save Macro", - showCancelConfirm = false, - onCancelConfirm, - showDelete = false, - onDelete, - isDeleting = false }: MacroFormProps) { const [macro, setMacro] = useState>(initialData); const [keyQueries, setKeyQueries] = useState>({}); const [errors, setErrors] = useState({}); const [errorMessage, setErrorMessage] = useState(null); - const [showDeleteConfirm, setShowDeleteConfirm] = useState(false); const showTemporaryError = (message: string) => { setErrorMessage(message); @@ -261,70 +249,23 @@ export function MacroForm({ )} -
- {showCancelConfirm ? ( -
- - Cancel changes? - -
- ) : ( - <> -
-
- {showDelete && ( -
- - setShowDeleteConfirm(false)} - title="Delete Macro" - description="Are you sure you want to delete this macro? This action cannot be undone." - variant="danger" - confirmText={isDeleting ? "Deleting" : "Delete"} - onConfirm={() => { - onDelete?.(); - setShowDeleteConfirm(false); - }} - isConfirming={isDeleting} - /> ); -} \ No newline at end of file +} \ No newline at end of file diff --git a/ui/src/components/MacroStepCard.tsx b/ui/src/components/MacroStepCard.tsx index 6d1dd5c..986eb38 100644 --- a/ui/src/components/MacroStepCard.tsx +++ b/ui/src/components/MacroStepCard.tsx @@ -1,4 +1,4 @@ -import { LuArrowUp, LuArrowDown, LuX } from "react-icons/lu"; +import { LuArrowUp, LuArrowDown, LuX, LuTrash } from "react-icons/lu"; import { Button } from "@/components/Button"; import { Combobox } from "@/components/Combobox"; @@ -6,7 +6,7 @@ import { SelectMenuBasic } from "@/components/SelectMenuBasic"; import Card from "@/components/Card"; import { keys, modifiers, keyDisplayMap } from "@/keyboardMappings"; import { MAX_KEYS_PER_STEP } from "@/constants/macros"; -import FieldLabel from "@/components/FieldLabel";1 +import FieldLabel from "@/components/FieldLabel"; // Filter out modifier keys since they're handled in the modifiers section const modifierKeyPrefixes = ['Alt', 'Control', 'Shift', 'Meta']; @@ -121,6 +121,7 @@ export function MacroStepCard({ size="XS" theme="danger" text="Delete" + LeadingIcon={LuTrash} onClick={onDelete} /> )} @@ -138,28 +139,20 @@ export function MacroStepCard({
{mods.map(option => ( - - { - const modifiersArray = ensureArray(step.modifiers); - const newModifiers = e.target.checked - ? [...modifiersArray, option.value] - : modifiersArray.filter(m => m !== option.value); - onModifierChange(newModifiers); - }} - /> - {option.label.split(' ')[1] || option.label} - +
@@ -169,7 +162,7 @@ export function MacroStepCard({
- +
{ensureArray(step.keys).map((key, keyIndex) => ( diff --git a/ui/src/routes/devices.$id.settings.macros.edit.tsx b/ui/src/routes/devices.$id.settings.macros.edit.tsx index 6d92420..c4b47c2 100644 --- a/ui/src/routes/devices.$id.settings.macros.edit.tsx +++ b/ui/src/routes/devices.$id.settings.macros.edit.tsx @@ -1,10 +1,13 @@ import { useNavigate, useParams } from "react-router-dom"; import { useState, useEffect } from "react"; +import { LuTrash } from "react-icons/lu"; import { KeySequence, useMacrosStore } from "@/hooks/stores"; import { SettingsPageHeader } from "@/components/SettingsPageheader"; import { MacroForm } from "@/components/MacroForm"; import notifications from "@/notifications"; +import { Button } from "@/components/Button"; +import { ConfirmDialog } from "@/components/ConfirmDialog"; const normalizeSortOrders = (macros: KeySequence[]): KeySequence[] => { return macros.map((macro, index) => ({ @@ -20,6 +23,7 @@ export default function SettingsMacrosEditRoute() { const navigate = useNavigate(); const { macroId } = useParams<{ macroId: string }>(); const [macro, setMacro] = useState(null); + const [showDeleteConfirm, setShowDeleteConfirm] = useState(false); useEffect(() => { const foundMacro = macros.find(m => m.id === macroId); @@ -89,19 +93,40 @@ export default function SettingsMacrosEditRoute() { return (
- +
+ +
navigate("../")} isSubmitting={isUpdating} submitText="Save Changes" - showDelete - onDelete={handleDeleteMacro} - isDeleting={isDeleting} + /> + + setShowDeleteConfirm(false)} + title="Delete Macro" + description="Are you sure you want to delete this macro? This action cannot be undone." + variant="danger" + confirmText={isDeleting ? "Deleting" : "Delete"} + onConfirm={() => { + handleDeleteMacro(); + setShowDeleteConfirm(false); + }} + isConfirming={isDeleting} />
); diff --git a/ui/src/routes/devices.$id.settings.macros.tsx b/ui/src/routes/devices.$id.settings.macros.tsx index 88e59da..744fd2a 100644 --- a/ui/src/routes/devices.$id.settings.macros.tsx +++ b/ui/src/routes/devices.$id.settings.macros.tsx @@ -1,6 +1,6 @@ import { useEffect, Fragment, useMemo, useState, useCallback } from "react"; import { useNavigate } from "react-router-dom"; -import { LuPenLine, LuLoader, LuCopy, LuMoveRight, LuCornerDownRight, LuArrowUp, LuArrowDown } from "react-icons/lu"; +import { LuPenLine, LuLoader, LuCopy, LuMoveRight, LuCornerDownRight, LuArrowUp, LuArrowDown, LuTrash } from "react-icons/lu"; import { KeySequence, useMacrosStore, generateMacroId } from "@/hooks/stores"; import { SettingsPageHeader } from "@/components/SettingsPageheader"; @@ -10,7 +10,7 @@ import Card from "@/components/Card"; import { MAX_TOTAL_MACROS, COPY_SUFFIX } from "@/constants/macros"; import { keyDisplayMap, modifierDisplayMap } from "@/keyboardMappings"; import notifications from "@/notifications"; -import { SettingsItem } from "@/routes/devices.$id.settings"; +import { ConfirmDialog } from "@/components/ConfirmDialog"; const normalizeSortOrders = (macros: KeySequence[]): KeySequence[] => { return macros.map((macro, index) => ({ @@ -23,6 +23,8 @@ export default function SettingsMacrosRoute() { const { macros, loading, initialized, loadMacros, saveMacros } = useMacrosStore(); const navigate = useNavigate(); const [actionLoadingId, setActionLoadingId] = useState(null); + const [showDeleteConfirm, setShowDeleteConfirm] = useState(false); + const [macroToDelete, setMacroToDelete] = useState(null); const isMaxMacrosReached = useMemo(() => macros.length >= MAX_TOTAL_MACROS, @@ -98,6 +100,27 @@ export default function SettingsMacrosRoute() { } }, [macros, saveMacros, setActionLoadingId]); + const handleDeleteMacro = useCallback(async () => { + if (!macroToDelete?.id) return; + + setActionLoadingId(macroToDelete.id); + try { + const updatedMacros = normalizeSortOrders(macros.filter(m => m.id !== macroToDelete.id)); + await saveMacros(updatedMacros); + notifications.success(`Macro "${macroToDelete.name}" deleted successfully`); + setShowDeleteConfirm(false); + setMacroToDelete(null); + } catch (error: unknown) { + if (error instanceof Error) { + notifications.error(`Failed to delete macro: ${error.message}`); + } else { + notifications.error("Failed to delete macro"); + } + } finally { + setActionLoadingId(null); + } + }, [macroToDelete, macros, saveMacros]); + const MacroList = useMemo(() => (
{macros.map((macro, index) => ( @@ -176,11 +199,21 @@ export default function SettingsMacrosRoute() {
+
))} + + { + setShowDeleteConfirm(false); + setMacroToDelete(null); + }} + title="Delete Macro" + description={`Are you sure you want to delete "${macroToDelete?.name}"? This action cannot be undone.`} + variant="danger" + confirmText={actionLoadingId === macroToDelete?.id ? "Deleting..." : "Delete"} + onConfirm={handleDeleteMacro} + isConfirming={actionLoadingId === macroToDelete?.id} + />
- ), [macros, actionLoadingId]); + ), [macros, actionLoadingId, showDeleteConfirm, macroToDelete, handleDeleteMacro]); return (
{macros.length > 0 && ( - <> +
-
- - { macros.length > 0 && ( -
-
- )} -
-
- + { macros.length > 0 && ( +
+
+ )} +
)}