diff --git a/src/vertex/app/changelog.md b/src/vertex/app/changelog.md index d87503fbea..6e8cfeb24c 100644 --- a/src/vertex/app/changelog.md +++ b/src/vertex/app/changelog.md @@ -4,6 +4,50 @@ --- +## Version 1.23.44 +**Released:** May 19, 2026 + +### Device Management Scoped Banner Migration & Mutation Callback Cleanup + +Migrated feedback notifications from global floating toasts (`ReusableToast`) to context-aware `InfoBanner` components (`useBanner`) inside device management modules to keep alerts inline and centered within their active modal or dialog containers. Refactored React Query hook callbacks to push feedback responsibility to the UI components. + +
+Device Management — Scoped Banner Migration (7) + +- **Create Device Modal**: Integrated `useBanner` for scoped validation and creation alerts. The `showBanner` utility replaces the old inline red error block for missing network errors. +- **Import Device Modal**: Integrated `useBanner` to surface a previously silent missing user ID warning and errors. Replaced old inline red error alerts and fixed a critical TypeScript compilation error where `importDevice.mutate()` was called with 3 arguments instead of 1-2 by merging the `onSuccess` and `onError` options into a single second argument. Added missing `getApiErrorMessage` import. +- **Add Maintenance Log Modal**: Replaced `ReusableToast` with scoped `showBanner` for validation errors to align modal design feedback. +- **Deploy Device Component**: Swapped 3 occurrences of `ReusableToast` with `showBanner` alerts. +- **Recall Device Dialog**: Added `useBanner` support and shifted all success and error UI notifications from raw hook callbacks directly to try/catch blocks within the component. +- **Device Details Modal**: Migrated all validation, detail update, and key decryption feedback from floating toasts to `showBanner`. Added local callback overrides to the `updateLocal` and `updateGlobal` mutate invocations. +- **Device Assignment Modal**: Added `useBanner` with `scoped: false` (since this dialog extends base Radix `Dialog` rather than `ReusableDialog`), keeping assignment feedback in the modal itself instead of hook side-effects. + +
+ +
+Custom Hook & Callback Separation (1) + +- **useDevices Hooks Overhaul**: Removed imperative `ReusableToast` side-effects from `useRecallDevice`, `useUpdateDeviceLocal`, `useUpdateDeviceGlobal`, `useAssignDeviceToOrganization`, and `useDecryptDeviceKeys`. Hooks are now clean, focused solely on API interactions and cache invalidation. + +
+ +
+Files Modified (8) + +- `src/vertex/app/changelog.md` [MODIFIED] +- `src/vertex/components/features/devices/create-device-modal.tsx` [MODIFIED] +- `src/vertex/components/features/devices/import-device-modal.tsx` [MODIFIED] +- `src/vertex/components/features/devices/add-maintenance-log-modal.tsx` [MODIFIED] +- `src/vertex/components/features/devices/deploy-device-component.tsx` [MODIFIED] +- `src/vertex/components/features/devices/recall-device-dialog.tsx` [MODIFIED] +- `src/vertex/components/features/devices/device-details-modal.tsx` [MODIFIED] +- `src/vertex/components/features/devices/device-assignment-modal.tsx` [MODIFIED] +- `src/vertex/core/hooks/useDevices.ts` [MODIFIED] + +
+ +--- + ## Version 1.23.43 **Released:** May 17, 2026 diff --git a/src/vertex/components/features/devices/add-maintenance-log-modal.tsx b/src/vertex/components/features/devices/add-maintenance-log-modal.tsx index 477f9b58fc..fe6a7917cf 100644 --- a/src/vertex/components/features/devices/add-maintenance-log-modal.tsx +++ b/src/vertex/components/features/devices/add-maintenance-log-modal.tsx @@ -9,7 +9,8 @@ import { useUserContext } from "@/core/hooks/useUserContext"; import { MultiSelectCombobox } from "@/components/ui/multi-select"; import { Switch } from "@/components/ui/switch"; import ReusableDialog from "@/components/shared/dialog/ReusableDialog"; -import ReusableToast from "@/components/shared/toast/ReusableToast"; +import { useBanner } from "@/context/banner-context"; +import { getApiErrorMessage } from "@/core/utils/getApiErrorMessage"; interface AddMaintenanceLogModalProps { open: boolean; @@ -51,6 +52,7 @@ const AddMaintenanceLogModal: React.FC = ({ open, o const [maintenanceType, setMaintenanceType] = useState<"preventive" | "corrective">("preventive"); const { userDetails } = useUserContext(); + const { showBanner } = useBanner(); const addMaintenanceLog = useAddMaintenanceLog(); // Reset form when modal opens/closes @@ -65,7 +67,7 @@ const AddMaintenanceLogModal: React.FC = ({ open, o const handleSubmit = async () => { if (!date || selectedTags.length === 0) { - ReusableToast({message: "Please fill all fields", type:"ERROR"}) + showBanner({ severity: 'error', message: 'Please fill all required fields', scoped: true }); return; } @@ -81,8 +83,20 @@ const AddMaintenanceLogModal: React.FC = ({ open, o user_id: userDetails?._id || "", }; - await addMaintenanceLog.mutateAsync({ deviceName, logData }); - onOpenChange(false); + try { + await addMaintenanceLog.mutateAsync({ deviceName, logData }); + setTimeout(() => { + showBanner({ + severity: 'success', + title: 'Success', + message: `Maintenance log for ${deviceName} has been added successfully.`, + scoped: false + }); + }, 300); + onOpenChange(false); + } catch (error) { + showBanner({ severity: 'error', message: `Failed to Add Maintenance Log: ${getApiErrorMessage(error)}`, scoped: true }); + } }; return ( diff --git a/src/vertex/components/features/devices/create-device-modal.tsx b/src/vertex/components/features/devices/create-device-modal.tsx index 9d782806af..9f6b0bfb0d 100644 --- a/src/vertex/components/features/devices/create-device-modal.tsx +++ b/src/vertex/components/features/devices/create-device-modal.tsx @@ -10,6 +10,8 @@ import ReusableSelectInput from "@/components/shared/select/ReusableSelectInput" import { MultiSelectCombobox } from "@/components/ui/multi-select"; import { DEFAULT_DEVICE_TAGS } from "@/core/constants/devices"; import { Label } from "@/components/ui/label"; +import { useBanner } from "@/context/banner-context"; +import { getApiErrorMessage } from "@/core/utils/getApiErrorMessage"; interface CreateDeviceModalProps { open: boolean; @@ -30,6 +32,7 @@ const CreateDeviceModal: React.FC = ({ }); const [errors, setErrors] = useState>({}); + const { showBanner } = useBanner(); const activeNetwork = useAppSelector((state) => state.user.activeNetwork); const createDevice = useCreateDevice(); @@ -56,7 +59,7 @@ const CreateDeviceModal: React.FC = ({ const effectiveNetworkName = networkName || activeNetwork?.net_name; if (!effectiveNetworkName) { - setErrors({ general: "No active Sensor Manufacturer found" }); + showBanner({ severity: 'error', message: 'No active Sensor Manufacturer found', scoped: true }); return; } @@ -67,20 +70,26 @@ const CreateDeviceModal: React.FC = ({ description: formData.description.trim() || undefined, network: effectiveNetworkName, tags: formData.tags, + }, { + onSuccess: (data, variables) => { + setTimeout(() => { + showBanner({ + severity: 'success', + title: 'Success', + message: `${variables.long_name.trim()} has been created successfully.`, + scoped: false + }); + }, 300); + setFormData({ long_name: "", category: "", description: "", tags: [] }); + setErrors({}); + onOpenChange(false); + }, + onError: (error) => { + showBanner({ severity: 'error', message: `Creation Failed: ${getApiErrorMessage(error)}`, scoped: true }); + }, }); - - // Reset form and close modal - setFormData({ - long_name: "", - category: "", - description: "", - tags: [], - }); - setErrors({}); - onOpenChange(false); } catch (error) { - // Error handling is done in the hook - console.error("Create device failed:", error); + showBanner({ severity: 'error', message: `Creation Failed: ${getApiErrorMessage(error)}`, scoped: true }); } }; @@ -136,11 +145,6 @@ const CreateDeviceModal: React.FC = ({ }} >
- {errors.general && ( -
- {errors.general} -
- )} import("../mini-map/mini-map")); @@ -427,6 +428,7 @@ const DeployDeviceComponent = ({ onDeploymentError }: DeployDeviceComponentProps) => { const queryClient = useQueryClient(); + const { showBanner } = useBanner(); const { userScope, userDetails } = useUserContext(); const { devices: allDevices } = useDevices({ enabled: userScope !== 'personal' }); const [currentStep, setCurrentStep] = React.useState(0); @@ -608,10 +610,7 @@ const DeployDeviceComponent = ({ const handleNext = (): void => { if (currentStep === 0 && !validateDeviceDetails()) { - ReusableToast({ - type: "ERROR", - message: "Incomplete Details: Please fill in all required device details.", - }); + showBanner({ severity: 'error', message: 'Incomplete Details: Please fill in all required device details.', scoped: true }); return; } setCurrentStep((prev) => Math.min(prev + 1, siteSource === 'new' ? 2 : 1)); @@ -642,18 +641,12 @@ const DeployDeviceComponent = ({ const handleDeploy = (): void => { if (!userDetails?._id) { - ReusableToast({ - type: "ERROR", - message: "User information not available. Please reload the page.", - }); + showBanner({ severity: 'error', message: 'User information not available. Please reload the page.', scoped: true }); return; } if (siteSource === 'previous' && !deviceData.site_id) { - ReusableToast({ - type: "ERROR", - message: "Select a previous site to continue.", - }); + showBanner({ severity: 'error', message: 'Select a previous site to continue.', scoped: true }); return; } @@ -686,7 +679,15 @@ const DeployDeviceComponent = ({ queryClient.invalidateQueries({ queryKey: ["device-details", prefilledDevice._id] }); } - // On successful deployment, reset form fields + setTimeout(() => { + showBanner({ + severity: 'success', + title: 'Success', + message: `${deviceData.deviceName} has been deployed successfully.`, + scoped: false + }); + }, 300); + setDeviceData({ deviceName: "", deployment_date: undefined, @@ -709,6 +710,22 @@ const DeployDeviceComponent = ({ if (onClose) onClose(); }, onError: (error) => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const errorData = (error as any)?.response?.data; + let errorMessage = getApiErrorMessage(error); + + if (errorData?.failed_deployments?.length > 0) { + const failedMessages = errorData.failed_deployments + // eslint-disable-next-line @typescript-eslint/no-explicit-any + .map((deployment: any) => deployment.error?.message) + .filter(Boolean); + + if (failedMessages.length > 0) { + errorMessage = failedMessages.join(', '); + } + } + + showBanner({ severity: 'error', message: `Deployment Failed: ${errorMessage}`, scoped: true }); onDeploymentError?.(error instanceof Error ? error : new Error("Unknown error")); }, } diff --git a/src/vertex/components/features/devices/device-assignment-modal.tsx b/src/vertex/components/features/devices/device-assignment-modal.tsx index 889d4b7e62..651d68e060 100644 --- a/src/vertex/components/features/devices/device-assignment-modal.tsx +++ b/src/vertex/components/features/devices/device-assignment-modal.tsx @@ -13,6 +13,8 @@ import { Button } from "@/components/ui/button"; import { useAppSelector } from "@/core/redux/hooks"; import { Device } from "@/app/types/devices"; import { useAssignDeviceToOrganization } from "@/core/hooks/useDevices"; +import { useBanner } from "@/context/banner-context"; +import { getApiErrorMessage } from "@/core/utils/getApiErrorMessage"; import { Label } from "@/components/ui/label"; import { ComboBox } from "@/components/ui/combobox"; import { useRouter } from "next/navigation"; @@ -45,16 +47,28 @@ const DeviceAssignmentModal: React.FC = ({ const [selectedDevice, setSelectedDevice] = useState(""); const assignDevice = useAssignDeviceToOrganization(); + const { showBanner } = useBanner(); const handleAssign = async () => { if (!selectedOrganization || !selectedDevice || !userDetails?._id) return; - await assignDevice.mutateAsync({ - device_name: selectedDevice, - organization_id: selectedOrganization, - user_id: userDetails._id, - }); - onSuccess(); + try { + const data = await assignDevice.mutateAsync({ + device_name: selectedDevice, + organization_id: selectedOrganization, + user_id: userDetails._id, + }); + setTimeout(() => { + showBanner({ + severity: 'success', + title: 'Success', + message: `${data.device.name} has been assigned successfully.`, + }); + }, 300); + onSuccess(); + } catch (error) { + showBanner({ severity: 'error', message: `Assignment Failed: ${getApiErrorMessage(error)}`, scoped: false }); + } }; const handleClose = () => { diff --git a/src/vertex/components/features/devices/device-details-modal.tsx b/src/vertex/components/features/devices/device-details-modal.tsx index 8e468d8fb0..3fde74c5b3 100644 --- a/src/vertex/components/features/devices/device-details-modal.tsx +++ b/src/vertex/components/features/devices/device-details-modal.tsx @@ -14,7 +14,8 @@ import ReusableButton from "@/components/shared/button/ReusableButton"; import ReusableSelectInput from "@/components/shared/select/ReusableSelectInput"; import ReusableInputField from "@/components/shared/inputfield/ReusableInputField"; import { AqEdit01 as AqEdit, AqKey01 } from "@airqo/icons-react"; -import ReusableToast from "@/components/shared/toast/ReusableToast"; +import { useBanner } from "@/context/banner-context"; +import { getApiErrorMessage } from "@/core/utils/getApiErrorMessage"; import logger from "@/lib/logger"; interface DeviceDetailsModalProps { @@ -60,6 +61,7 @@ const DeviceDetailsModal: React.FC = ({ open, device, o const updateLocal = useUpdateDeviceLocal(); const updateGlobal = useUpdateDeviceGlobal(); const decryptKeys = useDecryptDeviceKeys(); + const { showBanner } = useBanner(); const form = useForm({ resolver: zodResolver(deviceUpdateSchema), @@ -132,7 +134,7 @@ const DeviceDetailsModal: React.FC = ({ open, device, o const onSubmitLocal = async (data: DeviceUpdateFormData) => { if (!device?._id) { - ReusableToast({ message: "Cannot update device: missing device ID", type: "WARNING" }); + showBanner({ severity: 'warning', message: "Cannot update device: missing device ID", scoped: true }); return; } @@ -168,16 +170,20 @@ const DeviceDetailsModal: React.FC = ({ open, device, o { deviceId: device._id, deviceData: processedData }, { onSuccess: () => { + showBanner({ severity: 'success', message: 'Device information has been updated locally.', scoped: true }); setIsEditMode(false); form.reset(data); }, + onError: (error) => { + showBanner({ severity: 'error', message: `Update Failed: ${getApiErrorMessage(error)}`, scoped: true }); + }, } ); }; const onSubmitGlobal = async (data: DeviceUpdateFormData) => { if (!device?._id) { - ReusableToast({ message: "Cannot update device: missing device ID", type: "WARNING" }); + showBanner({ severity: 'warning', message: "Cannot update device: missing device ID", scoped: true }); return; } @@ -213,9 +219,13 @@ const DeviceDetailsModal: React.FC = ({ open, device, o { deviceId: device._id, deviceData: processedData }, { onSuccess: () => { + showBanner({ severity: 'success', message: 'Device information has been updated globally.', scoped: true }); setIsEditMode(false); form.reset(data); }, + onError: (error) => { + showBanner({ severity: 'error', message: `Sync Failed: ${getApiErrorMessage(error)}`, scoped: true }); + }, } ); }; @@ -224,9 +234,9 @@ const DeviceDetailsModal: React.FC = ({ open, device, o if (valueToCopy !== undefined && valueToCopy !== null) { try { await navigator.clipboard?.writeText(String(valueToCopy)) - ReusableToast({ message: "Decrypted Key Copied", type: "SUCCESS" }) + showBanner({ severity: 'success', message: "Decrypted Key Copied", scoped: true }); } catch (err) { - ReusableToast({ message: `Failed to copy key: ${String(err)}`, type: "ERROR" }) + showBanner({ severity: 'error', message: `Failed to copy key: ${String(err)}`, scoped: true }); } } } @@ -236,7 +246,7 @@ const DeviceDetailsModal: React.FC = ({ open, device, o const deviceNumber = device.device_number; if (!encryptedKey || !deviceNumber) { - ReusableToast({ message: `No ${keyType} or device number available for decryption.`, type: "WARNING" }); + showBanner({ severity: 'warning', message: `No ${keyType} or device number available for decryption.`, scoped: true }); return; } @@ -251,10 +261,11 @@ const DeviceDetailsModal: React.FC = ({ open, device, o if (response.success && response.decrypted_keys && response.decrypted_keys.length > 0) { handleCopy(response.decrypted_keys[0].decrypted_key); } else { - ReusableToast({ message: "Decryption failed or no key returned.", type: "ERROR" }); + showBanner({ severity: 'error', message: "Decryption failed or no key returned.", scoped: true }); } - } catch { - logger.info("Decryption failed"); // simple log + } catch (error) { + showBanner({ severity: 'error', message: `Decryption Failed: ${getApiErrorMessage(error)}`, scoped: true }); + logger.info("Decryption failed"); } }; diff --git a/src/vertex/components/features/devices/import-device-modal.tsx b/src/vertex/components/features/devices/import-device-modal.tsx index aee08f5417..a757485705 100644 --- a/src/vertex/components/features/devices/import-device-modal.tsx +++ b/src/vertex/components/features/devices/import-device-modal.tsx @@ -13,10 +13,13 @@ import { useGroupCohorts } from "@/core/hooks/useCohorts"; import { useAppSelector } from "@/core/redux/hooks"; import { usePathname } from "next/navigation"; import logger from "@/lib/logger"; +import { useBanner } from "@/context/banner-context"; import { NetworkRequestDialog } from "../networks/network-request-dialog"; import { MultiSelectCombobox } from "@/components/ui/multi-select"; import { DEFAULT_DEVICE_TAGS } from "@/core/constants/devices"; import { Label } from "@/components/ui/label"; +import { getApiErrorMessage } from "@/core/utils/getApiErrorMessage"; + interface ImportDeviceModalProps { open: boolean; @@ -45,6 +48,7 @@ const ImportDeviceModal: React.FC = ({ const [showMore, setShowMore] = useState(false); const [isRequestDialogOpen, setIsRequestDialogOpen] = useState(false); const [errors, setErrors] = useState>({}); + const { showBanner } = useBanner(); const importDevice = useImportDevice(); const { networks, isLoading: isLoadingNetworks } = useNetworks(); @@ -108,6 +112,7 @@ const ImportDeviceModal: React.FC = ({ if (!userId) { logger.warn("User ID is missing"); + showBanner({ severity: 'error', message: 'Unable to identify user. Please reload and try again.', scoped: true }); return; } @@ -117,7 +122,22 @@ const ImportDeviceModal: React.FC = ({ user_id: userId, ...(cohortId && { cohort_id: cohortId }), }, - { onSuccess: () => onOpenChange(false) } + { + onSuccess: (data, variables) => { + onOpenChange(false); + setTimeout(() => { + showBanner({ + severity: 'success', + title: 'Success', + message: `${variables.long_name.trim()} has been imported successfully.`, + scoped: false + }); + }, 300); + }, + onError: (error) => { + showBanner({ severity: 'error', message: `Import Failed: ${getApiErrorMessage(error)}`, scoped: true }); + }, + } ); }; @@ -180,11 +200,6 @@ const ImportDeviceModal: React.FC = ({ }} >
- {errors.general && ( -
- {errors.general} -
- )} (""); const recallDevice = useRecallDevice(); const { userDetails } = useUserContext(); + const { showBanner } = useBanner(); const handleRecall = async () => { if (!recallType || !userDetails?._id) { @@ -46,13 +49,18 @@ export default function RecallDeviceDialog({ userName: userDetails.userName, }, }); - - // Reset form and close dialog + setTimeout(() => { + showBanner({ + severity: 'success', + title: 'Success', + message: `${deviceDisplayName || deviceName} has been recalled successfully.`, + scoped: false + }); + }, 300); setRecallType(""); onOpenChange(false); } catch (error) { - // Error handling is done in the hook - console.error("Recall failed:", error); + showBanner({ severity: 'error', message: `Recall Failed: ${getApiErrorMessage(error)}`, scoped: true }); } }; diff --git a/src/vertex/core/hooks/useDevices.ts b/src/vertex/core/hooks/useDevices.ts index e130c3b09c..6aa7c09f29 100644 --- a/src/vertex/core/hooks/useDevices.ts +++ b/src/vertex/core/hooks/useDevices.ts @@ -349,20 +349,10 @@ export const useAssignDeviceToOrganization = () => { DeviceAssignmentRequest >({ mutationFn: devices.assignDeviceToOrganization, - onSuccess: data => { - ReusableToast({ - message: `${data.device.name} has been assigned to the organization.`, - type: 'SUCCESS', - }); + onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['myDevices'] }); queryClient.invalidateQueries({ queryKey: ['devices'] }); }, - onError: error => { - ReusableToast({ - message: `Assignment Failed: ${getApiErrorMessage(error)}`, - type: 'ERROR', - }); - }, }); }; @@ -413,21 +403,11 @@ export const useUpdateDeviceLocal = () => { deviceData: Partial; }) => devices.updateDeviceLocal(deviceId, deviceData), onSuccess: (data, variables) => { - ReusableToast({ - message: 'Device information has been updated locally.', - type: 'SUCCESS', - }); queryClient.invalidateQueries({ queryKey: ['device-details', variables.deviceId], }); queryClient.invalidateQueries({ queryKey: ['devices'] }); }, - onError: (error: AxiosError) => { - ReusableToast({ - message: `Update Failed: ${getApiErrorMessage(error)}`, - type: 'ERROR', - }); - }, }); }; @@ -443,21 +423,11 @@ export const useUpdateDeviceGlobal = () => { deviceData: Partial; }) => devices.updateDeviceGlobal(deviceId, deviceData), onSuccess: (data, variables) => { - ReusableToast({ - message: 'Device information has been updated globally.', - type: 'SUCCESS', - }); queryClient.invalidateQueries({ queryKey: ['device-details', variables.deviceId], }); queryClient.invalidateQueries({ queryKey: ['devices'] }); }, - onError: (error: AxiosError) => { - ReusableToast({ - message: `Sync Failed: ${getApiErrorMessage(error)}`, - type: 'ERROR', - }); - }, }); }; @@ -517,11 +487,7 @@ export const useCreateDevice = () => { }; return devices.createDevice(payload); }, - onSuccess: (data, variables) => { - ReusableToast({ - message: `${variables.long_name} has been created.`, - type: 'SUCCESS', - }); + onSuccess: (data) => { if (data.created_device && activeGroup?.grp_title) { updateDeviceGroup.mutate({ deviceId: data.created_device._id || '', @@ -532,12 +498,6 @@ export const useCreateDevice = () => { queryClient.invalidateQueries({ queryKey: ['network-devices'] }); queryClient.invalidateQueries({ queryKey: ['deviceActivities'] }); }, - onError: error => { - ReusableToast({ - message: `Creation Failed: ${getApiErrorMessage(error)}`, - type: 'ERROR', - }); - }, }); }; @@ -575,12 +535,7 @@ export const useImportDevice = () => { }; return devices.importDevice(payload); }, - onSuccess: (data, variables) => { - ReusableToast({ - message: `${variables.long_name} has been imported.`, - type: 'SUCCESS', - }); - + onSuccess: () => { // Refresh based on active module if (isAdminModule) { queryClient.invalidateQueries({ queryKey: ['network-devices'] }); @@ -595,13 +550,7 @@ export const useImportDevice = () => { queryClient.invalidateQueries({ queryKey: ['deviceActivities'] }); } } - }, - onError: error => { - ReusableToast({ - message: `Import Failed: ${getApiErrorMessage(error)}`, - type: 'ERROR', - }); - }, + } }); }; @@ -627,36 +576,12 @@ export const useDeployDevice = () => { email?: string; userName?: string; }) => devices.deployDevice(deviceData), - onSuccess: (data, variables) => { - ReusableToast({ - message: `${variables.deviceName} has been deployed.`, - type: 'SUCCESS', - }); + onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['devices'] }); queryClient.invalidateQueries({ queryKey: ['claimedDevices'] }); queryClient.invalidateQueries({ queryKey: ['myDevices'] }); queryClient.invalidateQueries({ queryKey: ['deviceActivities'] }); }, - onError: (error: AxiosError) => { - let errorMessage = getApiErrorMessage(error); - const errorData = error.response?.data as any; - - if (errorData?.failed_deployments?.length > 0) { - const failedMessages = errorData.failed_deployments - // eslint-disable-next-line @typescript-eslint/no-explicit-any - .map((deployment: any) => deployment.error?.message) - .filter(Boolean); - - if (failedMessages.length > 0) { - errorMessage = failedMessages.join(', '); - } - } - - ReusableToast({ - message: `Deployment Failed: ${errorMessage}`, - type: 'ERROR', - }); - }, }); }; @@ -680,21 +605,11 @@ export const useRecallDevice = () => { }; }) => devices.recallDevice(deviceName, recallData), onSuccess: (data, variables) => { - ReusableToast({ - message: `${variables.deviceName} has been recalled.`, - type: 'SUCCESS', - }); queryClient.invalidateQueries({ queryKey: ['devices'] }); queryClient.invalidateQueries({ queryKey: ['device-details'] }); queryClient.invalidateQueries({ queryKey: ['myDevices'] }); queryClient.invalidateQueries({ queryKey: ['deviceActivities'] }); }, - onError: error => { - ReusableToast({ - message: `Recall Failed: ${getApiErrorMessage(error)}`, - type: 'ERROR', - }); - }, }); }; @@ -710,21 +625,11 @@ export const useAddMaintenanceLog = () => { logData: MaintenanceLogData; }) => devices.addMaintenanceLog(deviceName, logData), onSuccess: (data, variables) => { - ReusableToast({ - message: `Maintenance log has been added for ${variables.deviceName}.`, - type: 'SUCCESS', - }); queryClient.invalidateQueries({ queryKey: ['devices'] }); queryClient.invalidateQueries({ queryKey: ['device-details'] }); queryClient.invalidateQueries({ queryKey: ['deviceStatus'] }); queryClient.invalidateQueries({ queryKey: ['deviceActivities'] }); }, - onError: error => { - ReusableToast({ - message: `Failed to Add Maintenance Log: ${getApiErrorMessage(error)}`, - type: 'ERROR', - }); - }, }); }; @@ -738,12 +643,6 @@ export const useDecryptDeviceKeys = () => { onSuccess: () => { logger.info('Keys decrypted successfully!'); }, - onError: error => { - ReusableToast({ - message: `Decryption Failed: ${getApiErrorMessage(error)}`, - type: 'ERROR', - }); - }, }); };