@plutonhq/core-frontend 0.1.29 → 0.1.31

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/dist-lib/@types/devices.d.ts +4 -4
  2. package/dist-lib/@types/devices.d.ts.map +1 -1
  3. package/dist-lib/@types/plans.d.ts +1 -0
  4. package/dist-lib/@types/plans.d.ts.map +1 -1
  5. package/dist-lib/components/App/Footer/Footer.d.ts +2 -1
  6. package/dist-lib/components/App/Footer/Footer.d.ts.map +1 -1
  7. package/dist-lib/components/App/Footer/Footer.js +10 -8
  8. package/dist-lib/components/App/Footer/Footer.js.map +1 -1
  9. package/dist-lib/components/Device/EditDevice/EditDevice.d.ts.map +1 -1
  10. package/dist-lib/components/Device/EditDevice/EditDevice.js +70 -61
  11. package/dist-lib/components/Device/EditDevice/EditDevice.js.map +1 -1
  12. package/dist-lib/components/Plan/BackupProgress/BackupProgress.d.ts.map +1 -1
  13. package/dist-lib/components/Plan/BackupProgress/BackupProgress.js +66 -68
  14. package/dist-lib/components/Plan/BackupProgress/BackupProgress.js.map +1 -1
  15. package/dist-lib/components/Plan/BackupProgress/BackupProgress.module.scss.js +40 -38
  16. package/dist-lib/components/Plan/BackupProgress/BackupProgress.module.scss.js.map +1 -1
  17. package/dist-lib/components/Plan/PlanForm/PlanForm.d.ts.map +1 -1
  18. package/dist-lib/components/Plan/PlanForm/PlanForm.js +20 -11
  19. package/dist-lib/components/Plan/PlanForm/PlanForm.js.map +1 -1
  20. package/dist-lib/components/Plan/PlanSettings/PlanAdvancedSettings.d.ts +2 -1
  21. package/dist-lib/components/Plan/PlanSettings/PlanAdvancedSettings.d.ts.map +1 -1
  22. package/dist-lib/components/Plan/PlanSettings/PlanAdvancedSettings.js +28 -21
  23. package/dist-lib/components/Plan/PlanSettings/PlanAdvancedSettings.js.map +1 -1
  24. package/dist-lib/components/Plan/PlanSettings/PlanGeneralSettings.d.ts +2 -1
  25. package/dist-lib/components/Plan/PlanSettings/PlanGeneralSettings.d.ts.map +1 -1
  26. package/dist-lib/components/Plan/PlanSettings/PlanGeneralSettings.js +21 -20
  27. package/dist-lib/components/Plan/PlanSettings/PlanGeneralSettings.js.map +1 -1
  28. package/dist-lib/components/Plan/PlanSettings/PlanScriptsSettings.d.ts.map +1 -1
  29. package/dist-lib/components/Plan/PlanSettings/PlanScriptsSettings.js +89 -101
  30. package/dist-lib/components/Plan/PlanSettings/PlanScriptsSettings.js.map +1 -1
  31. package/dist-lib/components/Plan/PlanSettings/PlanSourceSettings.d.ts.map +1 -1
  32. package/dist-lib/components/Plan/PlanSettings/PlanSourceSettings.js +31 -25
  33. package/dist-lib/components/Plan/PlanSettings/PlanSourceSettings.js.map +1 -1
  34. package/dist-lib/components/common/form/StoragePicker/StoragePicker.d.ts.map +1 -1
  35. package/dist-lib/components/common/form/StoragePicker/StoragePicker.js +52 -46
  36. package/dist-lib/components/common/form/StoragePicker/StoragePicker.js.map +1 -1
  37. package/dist-lib/components/common/form/Toggle/Toggle.d.ts +2 -1
  38. package/dist-lib/components/common/form/Toggle/Toggle.d.ts.map +1 -1
  39. package/dist-lib/components/common/form/Toggle/Toggle.js +21 -11
  40. package/dist-lib/components/common/form/Toggle/Toggle.js.map +1 -1
  41. package/dist-lib/components/common/form/Toggle/Toggle.module.scss.js +6 -4
  42. package/dist-lib/components/common/form/Toggle/Toggle.module.scss.js.map +1 -1
  43. package/dist-lib/hooks/usePlanSingleActions.d.ts.map +1 -1
  44. package/dist-lib/hooks/usePlanSingleActions.js +34 -33
  45. package/dist-lib/hooks/usePlanSingleActions.js.map +1 -1
  46. package/dist-lib/routes/PlanSingle/PlanSingle.d.ts.map +1 -1
  47. package/dist-lib/routes/PlanSingle/PlanSingle.js +76 -72
  48. package/dist-lib/routes/PlanSingle/PlanSingle.js.map +1 -1
  49. package/dist-lib/routes/PlanSingle/PlanSingle.module.scss.js +14 -12
  50. package/dist-lib/routes/PlanSingle/PlanSingle.module.scss.js.map +1 -1
  51. package/dist-lib/styles/core-frontend.css +1 -1
  52. package/dist-lib/utils/progressHelpers.d.ts.map +1 -1
  53. package/dist-lib/utils/progressHelpers.js +1 -0
  54. package/dist-lib/utils/progressHelpers.js.map +1 -1
  55. package/package.json +1 -1
  56. package/src/@types/devices.ts +4 -4
  57. package/src/@types/plans.ts +1 -0
  58. package/src/components/App/Footer/Footer.tsx +3 -2
  59. package/src/components/Device/EditDevice/EditDevice.tsx +11 -4
  60. package/src/components/Plan/BackupProgress/BackupProgress.module.scss +3 -0
  61. package/src/components/Plan/BackupProgress/BackupProgress.tsx +5 -3
  62. package/src/components/Plan/PlanForm/PlanForm.tsx +7 -1
  63. package/src/components/Plan/PlanSettings/PlanAdvancedSettings.tsx +7 -2
  64. package/src/components/Plan/PlanSettings/PlanGeneralSettings.tsx +3 -1
  65. package/src/components/Plan/PlanSettings/PlanScriptsSettings.tsx +29 -31
  66. package/src/components/Plan/PlanSettings/PlanSourceSettings.tsx +11 -3
  67. package/src/components/common/form/StoragePicker/StoragePicker.tsx +10 -2
  68. package/src/components/common/form/Toggle/Toggle.module.scss +7 -0
  69. package/src/components/common/form/Toggle/Toggle.tsx +14 -3
  70. package/src/hooks/usePlanSingleActions.tsx +4 -2
  71. package/src/routes/PlanSingle/PlanSingle.module.scss +10 -0
  72. package/src/routes/PlanSingle/PlanSingle.tsx +6 -1
  73. package/src/utils/progressHelpers.ts +1 -0
@@ -1 +1 @@
1
- {"version":3,"file":"progressHelpers.d.ts","sourceRoot":"","sources":["../../src/utils/progressHelpers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAEvD,wBAAgB,6BAA6B,CAAC,YAAY,EAAE,kBAAkB,GAAG,IAAI,GAAG,MAAM,CAqC7F;AAoCD,eAAO,MAAM,2BAA2B,GAAI,QAAQ,MAAM,EAAE,cAAc,MAAM,KAAG,MA4HlF,CAAC;AAEF,wBAAgB,8BAA8B,CAAC,YAAY,EAAE,kBAAkB,GAAG,IAAI,GAAG,MAAM,CAqC9F;AAED,eAAO,MAAM,4BAA4B,GAAI,QAAQ,MAAM,KAAG,MAiD7D,CAAC;AAEF,wBAAgB,iBAAiB,CAAC,YAAY,EAAE,kBAAkB,GAAG,IAAI,OAYxE;AAED;;;GAGG;AACH,wBAAgB,6BAA6B,CAAC,MAAM,EAAE;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,OAAO,CAAA;KAAE,EAAE,CAAA;CAAE,GAAG,MAAM,CAkBxI"}
1
+ {"version":3,"file":"progressHelpers.d.ts","sourceRoot":"","sources":["../../src/utils/progressHelpers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAEvD,wBAAgB,6BAA6B,CAAC,YAAY,EAAE,kBAAkB,GAAG,IAAI,GAAG,MAAM,CAqC7F;AAoCD,eAAO,MAAM,2BAA2B,GAAI,QAAQ,MAAM,EAAE,cAAc,MAAM,KAAG,MA6HlF,CAAC;AAEF,wBAAgB,8BAA8B,CAAC,YAAY,EAAE,kBAAkB,GAAG,IAAI,GAAG,MAAM,CAqC9F;AAED,eAAO,MAAM,4BAA4B,GAAI,QAAQ,MAAM,KAAG,MAiD7D,CAAC;AAEF,wBAAgB,iBAAiB,CAAC,YAAY,EAAE,kBAAkB,GAAG,IAAI,OAYxE;AAED;;;GAGG;AACH,wBAAgB,6BAA6B,CAAC,MAAM,EAAE;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,OAAO,CAAA;KAAE,EAAE,CAAA;CAAE,GAAG,MAAM,CAkBxI"}
@@ -63,6 +63,7 @@ const c = (t) => {
63
63
  PRE_BACKUP_START: "Preparing Backup...",
64
64
  PRE_BACKUP_DRY_RUN_START: "Performing Dry Run...",
65
65
  PRE_BACKUP_DRY_RUN_COMPLETE: "Dry Run Complete",
66
+ PRE_BACKUP_DRY_RUN_ERROR: "Dry Run Encountered Errors",
66
67
  PRE_BACKUP_CHECKS_START: "Running Checks...",
67
68
  PRE_BACKUP_CHECKS_COMPLETE: "Checks Complete",
68
69
  PRE_BACKUP_SCRIPTS_START: "Running Scripts...",
@@ -1 +1 @@
1
- {"version":3,"file":"progressHelpers.js","sources":["../../src/utils/progressHelpers.ts"],"sourcesContent":["import { BackupProgressData } from '../@types/backups';\r\n\r\nexport function generateBackupProgressMessage(progressData: BackupProgressData | null): string {\r\n if (!progressData?.events?.length) {\r\n return 'Initializing...';\r\n }\r\n\r\n // Find the last incomplete event or the most recent event\r\n const lastIncompleteEvent = progressData.events\r\n .slice()\r\n .reverse()\r\n .find((event) => !event.completed);\r\n\r\n const currentEvent = lastIncompleteEvent || progressData.events[progressData.events.length - 1];\r\n\r\n if (!currentEvent) {\r\n return 'Initializing...';\r\n }\r\n\r\n const { phase, action } = currentEvent;\r\n\r\n // Map phases to display names\r\n const phaseDisplayNames: Record<string, string> = {\r\n initializing: 'Initializing',\r\n 'pre-backup': 'Pre-Backup',\r\n backup: 'Backup',\r\n 'post-backup': 'Post-Backup',\r\n replicating: 'Replicating',\r\n finished: 'Complete',\r\n };\r\n\r\n const phaseDisplay = phaseDisplayNames[phase] || phase;\r\n const actionDisplay = getBackupEventActionMessage(action);\r\n\r\n if (phase === 'finished') {\r\n return actionDisplay;\r\n }\r\n\r\n return `${phaseDisplay}: ${actionDisplay}`;\r\n}\r\n\r\n// Helper function to handle script actions dynamically\r\nconst handleScriptAction = (action: string): string | null => {\r\n const scriptTypes = {\r\n ONBACKUPSTART: 'Start Script',\r\n ONBACKUPCOMPLETE: 'Complete Script',\r\n ONBACKUPERROR: 'Error Script',\r\n ONBACKUPFAILURE: 'Failure Script',\r\n ONBACKUPEND: 'End Script',\r\n };\r\n\r\n const actionStates = {\r\n START: (scriptType: string, num: string) => `Running ${scriptType} ${num}...`,\r\n COMPLETE: (_: string, num: string) => `Script ${num} Complete`,\r\n FAIL: (_: string, num: string) => `Script ${num} Failed`,\r\n ERROR: (scriptType: string, num: string) => `Error in ${scriptType} ${num}`,\r\n };\r\n\r\n // Generic pattern for all script actions: SCRIPTTYPE_SCRIPT_NUMBER_STATE\r\n const scriptMatch = action.match(/^(ONBACKUP\\w+)_SCRIPT_(\\d+)_(START|COMPLETE|FAIL|ERROR)$/);\r\n\r\n if (scriptMatch) {\r\n const [, scriptTypeKey, scriptNum, state] = scriptMatch;\r\n const scriptType = scriptTypes[scriptTypeKey as keyof typeof scriptTypes] || 'Script';\r\n const stateHandler = actionStates[state as keyof typeof actionStates];\r\n\r\n if (stateHandler) {\r\n return stateHandler(scriptType, scriptNum);\r\n }\r\n }\r\n\r\n return null;\r\n};\r\n\r\n// Function to extract numbers from action strings and create meaningful messages\r\nexport const getBackupEventActionMessage = (action: string, storageName?: string): string => {\r\n // Handle script actions first\r\n const scriptMessage = handleScriptAction(action);\r\n if (scriptMessage) {\r\n return scriptMessage;\r\n }\r\n\r\n // Handle retry attempts\r\n const retryAttemptMatch = action.match(/RETRY_ATTEMPT_(\\d+)_OF_(\\d+)_START/);\r\n if (retryAttemptMatch) {\r\n const [, current, total] = retryAttemptMatch;\r\n return `Retrying (${current}/${total})...`;\r\n }\r\n\r\n // Handle backup retry scheduling\r\n const backupRetryMatch = action.match(/BACKUP_RETRY_(\\d+)_OF_(\\d+)_SCHEDULED/);\r\n if (backupRetryMatch) {\r\n const [, current, total] = backupRetryMatch;\r\n return `Scheduling Retry (${parseInt(current)}/${total})...`;\r\n }\r\n\r\n // Handle replication retry scheduling\r\n const replicationRetryScheduledMatch = action.match(/REPLICATION_RETRY_(\\d+)_OF_(\\d+)_SCHEDULED/);\r\n if (replicationRetryScheduledMatch) {\r\n const [, current, total] = replicationRetryScheduledMatch;\r\n return `Retrying (${current}/${total})...`;\r\n }\r\n\r\n // Handle replication retry start\r\n const replicationRetryStartMatch = action.match(/REPLICATION_RETRY_(\\d+)_OF_(\\d+)_START/);\r\n if (replicationRetryStartMatch) {\r\n const [, current, total] = replicationRetryStartMatch;\r\n return `Retrying (${current}/${total})...`;\r\n }\r\n\r\n // Static action mappings\r\n const staticMessages: Record<string, string> = {\r\n INITIALIZE: 'Starting Backup...',\r\n PRE_BACKUP_START: 'Preparing Backup...',\r\n PRE_BACKUP_DRY_RUN_START: 'Performing Dry Run...',\r\n PRE_BACKUP_DRY_RUN_COMPLETE: 'Dry Run Complete',\r\n PRE_BACKUP_CHECKS_START: 'Running Checks...',\r\n PRE_BACKUP_CHECKS_COMPLETE: 'Checks Complete',\r\n PRE_BACKUP_SCRIPTS_START: 'Running Scripts...',\r\n PRE_BACKUP_SCRIPTS_COMPLETE: 'Scripts Complete',\r\n PRE_BACKUP_UNLOCK_STALE_LOCKS: 'Unlocking Repository...',\r\n PRE_BACKUP_COMPLETE: 'Pre-Backup Complete',\r\n BACKUP_OPERATION_START: 'Backing Up Files...',\r\n BACKUP_OPERATION_COMPLETE: 'Backup Complete',\r\n BACKUP_OPERATION_ERROR: 'Backup Encountered Errors',\r\n POST_BACKUP_START: 'Finalizing...',\r\n POST_BACKUP_SCRIPTS_START: 'Running Cleanup Scripts...',\r\n POST_BACKUP_SCRIPTS_COMPLETE: 'Cleanup Scripts Complete',\r\n POST_BACKUP_PRUNE_START: 'Pruning Old Backups...',\r\n POST_BACKUP_PRUNE_COMPLETE: 'Pruning Complete',\r\n POST_BACKUP_PRUNE_FAILED: 'Pruning Failed',\r\n POST_BACKUP_COMPLETE: 'Post-Backup Complete',\r\n POST_BACKUP_REPO_STATS_START: 'Updating Repository Statistics...',\r\n POST_BACKUP_REPO_STATS_COMPLETE: 'Repository Statistics Updated',\r\n POST_BACKUP_REPO_STATS_FAILED: 'Failed to Update Repository Statistics',\r\n TASK_COMPLETED: 'Completed Successfully',\r\n TASK_CANCELLED: 'Backup Cancelled by User.',\r\n TASK_FAILED: 'Backup Failed with Error.',\r\n FAILED_PERMANENTLY: 'Failed Permanently',\r\n ISO_CREATION_START: 'Creating ISO...',\r\n ISO_CREATION_COMPLETE: 'ISO Created Successfully',\r\n ISO_CREATION_FAILED: 'ISO Creation Failed',\r\n REMOTE_BACKUP_START: 'Backing Up Data...',\r\n REMOTE_BACKUP_COMPLETE: 'Data Backup Complete',\r\n REMOTE_BACKUP_FAILED: 'Data Backup Failed',\r\n ISO_ENCRYPTION_START: 'Encrypting ISO...',\r\n ISO_ENCRYPTION_COMPLETE: 'ISO Encryption Complete',\r\n ISO_UPLOAD_START: 'Uploading ISO...',\r\n ISO_UPLOAD_COMPLETE: 'ISO Upload Complete',\r\n ISO_UPLOAD_FAILED: 'ISO Upload Failed',\r\n BACKUP_WARNING: 'Hiccup Detected During Backup',\r\n // Replication actions\r\n REPLICATION_START: 'Replicating Snapshots...',\r\n REPLICATION_INIT_START: 'Initializing Replication Repository...',\r\n REPLICATION_INIT_COMPLETE: 'Replication Repository Initialized',\r\n REPLICATION_INIT_FAILED: 'Replication Repository Init Failed',\r\n REPLICATION_UNLOCK_START: 'Unlocking Replication Repository...',\r\n REPLICATION_UNLOCK_COMPLETE: 'Replication Repository Unlocked',\r\n REPLICATION_COPY_START: 'Copying Snapshot to Replication...',\r\n REPLICATION_COPY_COMPLETE: 'Snapshot Copied to Replication',\r\n REPLICATION_COPY_FAILED: 'Replication Copy Failed',\r\n REPLICATION_PRUNE_START: 'Pruning Replication Repository...',\r\n REPLICATION_PRUNE_COMPLETE: 'Replication Prune Complete',\r\n REPLICATION_PRUNE_FAILED: 'Replication Prune Failed',\r\n REPLICATION_STATS_START: 'Updating Replication Stats...',\r\n REPLICATION_STATS_COMPLETE: 'Replication Stats Updated',\r\n REPLICATION_COMPLETE: 'Replication Complete',\r\n REPLICATION_FAILED: 'Replication Failed',\r\n REPLICATION_PARTIAL_FAILURE: 'Some Replications Failed',\r\n REPLICATION_MANUAL_RETRY_START: 'Retrying Failed Replications...',\r\n REPLICATION_MANUAL_RETRY_COMPLETE: 'Replication Retry Complete',\r\n REPLICATION_MANUAL_RETRY_PARTIAL_FAILURE: 'Some Replication Retries Still Failed',\r\n };\r\n\r\n // Storage-name-aware replication action mappings\r\n const replicationMessagesWithStorage: Record<string, (name: string) => string> = {\r\n REPLICATION_START: (name) => `Replicating Snapshot to ${name}...`,\r\n REPLICATION_INIT_START: (name) => `Initializing Replication Repository in ${name}...`,\r\n REPLICATION_INIT_COMPLETE: (name) => `Replication Repository Initialized in ${name}`,\r\n REPLICATION_INIT_FAILED: (name) => `Replication Repository Init Failed in ${name}`,\r\n REPLICATION_UNLOCK_START: (name) => `Unlocking Replication Repository in ${name}...`,\r\n REPLICATION_UNLOCK_COMPLETE: (name) => `Replication Repository Unlocked in ${name}`,\r\n REPLICATION_COPY_START: (name) => `Copying Snapshot to ${name}...`,\r\n REPLICATION_COPY_COMPLETE: (name) => `Snapshot Copied to ${name}`,\r\n REPLICATION_COPY_FAILED: (name) => `Replication Copy Failed for ${name}`,\r\n REPLICATION_PRUNE_START: (name) => `Pruning Replication Repository in ${name}...`,\r\n REPLICATION_PRUNE_COMPLETE: (name) => `Replication Prune Complete for ${name}`,\r\n REPLICATION_PRUNE_FAILED: (name) => `Replication Prune Failed for ${name}`,\r\n REPLICATION_STATS_START: (name) => `Updating Replication Stats for ${name}...`,\r\n REPLICATION_STATS_COMPLETE: (name) => `Replication Stats Updated for ${name}`,\r\n REPLICATION_COMPLETE: (name) => `Replication Complete for ${name}`,\r\n REPLICATION_FAILED: (name) => `Replication Failed for ${name}`,\r\n };\r\n\r\n if (storageName && replicationMessagesWithStorage[action]) {\r\n return replicationMessagesWithStorage[action](storageName);\r\n }\r\n\r\n return staticMessages[action] || action;\r\n};\r\n\r\nexport function generateRestoreProgressMessage(progressData: BackupProgressData | null): string {\r\n if (!progressData?.events?.length) {\r\n return 'Initializing...';\r\n }\r\n\r\n // Find the last incomplete event or the most recent event\r\n const lastIncompleteEvent = progressData.events\r\n .slice()\r\n .reverse()\r\n .find((event) => !event.completed);\r\n\r\n const currentEvent = lastIncompleteEvent || progressData.events[progressData.events.length - 1];\r\n\r\n if (!currentEvent) {\r\n return 'Initializing...';\r\n }\r\n\r\n const { phase, action } = currentEvent;\r\n\r\n // Map phases to display names\r\n const phaseDisplayNames: Record<string, string> = {\r\n initializing: 'Initializing',\r\n 'pre-restore': 'Pre-Restore',\r\n restore: 'Restore',\r\n 'post-restore': 'Post-Restore',\r\n retry: 'Retrying',\r\n finished: 'Complete',\r\n };\r\n\r\n const phaseDisplay = phaseDisplayNames[phase] || phase;\r\n const actionDisplay = getRestoreEventActionMessage(action);\r\n\r\n if (phase === 'finished') {\r\n return actionDisplay;\r\n }\r\n\r\n return `${phaseDisplay}: ${actionDisplay}`;\r\n}\r\n\r\nexport const getRestoreEventActionMessage = (action: string): string => {\r\n // Handle script actions first\r\n const scriptMessage = handleScriptAction(action);\r\n if (scriptMessage) {\r\n return scriptMessage;\r\n }\r\n\r\n // Handle retry attempts\r\n const retryAttemptMatch = action.match(/RETRY_ATTEMPT_(\\d+)_OF_(\\d+)_START/);\r\n if (retryAttemptMatch) {\r\n const [, current, total] = retryAttemptMatch;\r\n return `Retrying (${current}/${total})...`;\r\n }\r\n\r\n // Handle backup retry scheduling\r\n const backupRetryMatch = action.match(/RESTORE_RETRY_(\\d+)_OF_(\\d+)_SCHEDULED/);\r\n if (backupRetryMatch) {\r\n const [, current, total] = backupRetryMatch;\r\n return `Scheduling Retry (${parseInt(current)}/${total})...`;\r\n }\r\n\r\n // Static action mappings\r\n const staticMessages: Record<string, string> = {\r\n INITIALIZE: 'Starting Restore...',\r\n PRE_RESTORE_START: 'Preparing Restore...',\r\n PRE_RESTORE_GET_SNAPSHOT: 'Getting Snapshot to Restore...',\r\n PRE_RESTORE_GET_SNAPSHOT_COMPLETE: 'Snapshot Retrieved',\r\n PRE_RESTORE_GET_SNAPSHOT_FAILED: 'Failed to Retrieve Snapshot',\r\n PRE_RESTORE_DRY_RUN_START: 'Performing Dry Run...',\r\n PRE_RESTORE_DRY_RUN_COMPLETE: 'Dry Run Complete',\r\n PRE_RESTORE_CHECKS_START: 'Running Checks...',\r\n PRE_RESTORE_CHECKS_COMPLETE: 'Checks Complete',\r\n PRE_RESTORE_UNLOCK_STALE_LOCKS: 'Unlocking Repository...',\r\n PRE_RESTORE_COMPLETE: 'Pre-Restore Complete',\r\n RESTORE_OPERATION_START: 'Restoring Files...',\r\n RESTORE_OPERATION_COMPLETE: 'Restore Complete',\r\n POST_RESTORE_START: 'Finalizing...',\r\n POST_RESTORE_COMPLETE: 'Post-Restore Complete',\r\n POST_RESTORE_REPO_STATS_START: 'Updating Repository Statistics...',\r\n POST_RESTORE_REPO_STATS_COMPLETE: 'Repository Statistics Updated',\r\n POST_RESTORE_WINDOWS_MOVE_START: 'Moving Restored files from temp directory to target path...',\r\n POST_RESTORE_WINDOWS_MOVE_ERROR: 'Failed to Move Restored files from temp directory to target path',\r\n POST_RESTORE_WINDOWS_MOVE_COMPLETE: 'Moved Restored files to target path',\r\n TASK_COMPLETED: 'Completed Successfully',\r\n TASK_FAILED: 'Restore Failed with Error.',\r\n TASK_CANCELLED: 'Restore Cancelled by User.',\r\n FAILED_PERMANENTLY: 'Failed Permanently',\r\n };\r\n return staticMessages[action] || action;\r\n};\r\n\r\nexport function extractResticData(progressData: BackupProgressData | null) {\r\n if (!progressData?.events?.length) {\r\n return null;\r\n }\r\n\r\n // Find the most recent event with resticData\r\n const eventWithResticData = progressData.events\r\n .slice()\r\n .reverse()\r\n .find((event) => event.resticData);\r\n\r\n return eventWithResticData?.resticData || null;\r\n}\r\n\r\n/**\r\n * Generates a human-readable progress message for a specific replication mirror.\r\n * Uses the mirror's latest event action and includes the storage name.\r\n */\r\nexport function generateMirrorProgressMessage(mirror: { storageName: string; events?: { action: string; completed: boolean }[] }): string {\r\n if (!mirror.events?.length) {\r\n return 'Pending...';\r\n }\r\n\r\n // Find the last incomplete event or the most recent event\r\n const lastIncompleteEvent = mirror.events\r\n .slice()\r\n .reverse()\r\n .find((event) => !event.completed);\r\n\r\n const currentEvent = lastIncompleteEvent || mirror.events[mirror.events.length - 1];\r\n\r\n if (!currentEvent) {\r\n return 'Pending...';\r\n }\r\n\r\n return getBackupEventActionMessage(currentEvent.action, mirror.storageName);\r\n}\r\n"],"names":["generateBackupProgressMessage","progressData","_a","currentEvent","event","phase","action","phaseDisplay","actionDisplay","getBackupEventActionMessage","handleScriptAction","scriptTypes","actionStates","scriptType","num","_","scriptMatch","scriptTypeKey","scriptNum","state","stateHandler","storageName","scriptMessage","retryAttemptMatch","current","total","backupRetryMatch","replicationRetryScheduledMatch","replicationRetryStartMatch","staticMessages","replicationMessagesWithStorage","name","generateRestoreProgressMessage","getRestoreEventActionMessage","extractResticData","eventWithResticData","generateMirrorProgressMessage","mirror"],"mappings":"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"}
1
+ {"version":3,"file":"progressHelpers.js","sources":["../../src/utils/progressHelpers.ts"],"sourcesContent":["import { BackupProgressData } from '../@types/backups';\r\n\r\nexport function generateBackupProgressMessage(progressData: BackupProgressData | null): string {\r\n if (!progressData?.events?.length) {\r\n return 'Initializing...';\r\n }\r\n\r\n // Find the last incomplete event or the most recent event\r\n const lastIncompleteEvent = progressData.events\r\n .slice()\r\n .reverse()\r\n .find((event) => !event.completed);\r\n\r\n const currentEvent = lastIncompleteEvent || progressData.events[progressData.events.length - 1];\r\n\r\n if (!currentEvent) {\r\n return 'Initializing...';\r\n }\r\n\r\n const { phase, action } = currentEvent;\r\n\r\n // Map phases to display names\r\n const phaseDisplayNames: Record<string, string> = {\r\n initializing: 'Initializing',\r\n 'pre-backup': 'Pre-Backup',\r\n backup: 'Backup',\r\n 'post-backup': 'Post-Backup',\r\n replicating: 'Replicating',\r\n finished: 'Complete',\r\n };\r\n\r\n const phaseDisplay = phaseDisplayNames[phase] || phase;\r\n const actionDisplay = getBackupEventActionMessage(action);\r\n\r\n if (phase === 'finished') {\r\n return actionDisplay;\r\n }\r\n\r\n return `${phaseDisplay}: ${actionDisplay}`;\r\n}\r\n\r\n// Helper function to handle script actions dynamically\r\nconst handleScriptAction = (action: string): string | null => {\r\n const scriptTypes = {\r\n ONBACKUPSTART: 'Start Script',\r\n ONBACKUPCOMPLETE: 'Complete Script',\r\n ONBACKUPERROR: 'Error Script',\r\n ONBACKUPFAILURE: 'Failure Script',\r\n ONBACKUPEND: 'End Script',\r\n };\r\n\r\n const actionStates = {\r\n START: (scriptType: string, num: string) => `Running ${scriptType} ${num}...`,\r\n COMPLETE: (_: string, num: string) => `Script ${num} Complete`,\r\n FAIL: (_: string, num: string) => `Script ${num} Failed`,\r\n ERROR: (scriptType: string, num: string) => `Error in ${scriptType} ${num}`,\r\n };\r\n\r\n // Generic pattern for all script actions: SCRIPTTYPE_SCRIPT_NUMBER_STATE\r\n const scriptMatch = action.match(/^(ONBACKUP\\w+)_SCRIPT_(\\d+)_(START|COMPLETE|FAIL|ERROR)$/);\r\n\r\n if (scriptMatch) {\r\n const [, scriptTypeKey, scriptNum, state] = scriptMatch;\r\n const scriptType = scriptTypes[scriptTypeKey as keyof typeof scriptTypes] || 'Script';\r\n const stateHandler = actionStates[state as keyof typeof actionStates];\r\n\r\n if (stateHandler) {\r\n return stateHandler(scriptType, scriptNum);\r\n }\r\n }\r\n\r\n return null;\r\n};\r\n\r\n// Function to extract numbers from action strings and create meaningful messages\r\nexport const getBackupEventActionMessage = (action: string, storageName?: string): string => {\r\n // Handle script actions first\r\n const scriptMessage = handleScriptAction(action);\r\n if (scriptMessage) {\r\n return scriptMessage;\r\n }\r\n\r\n // Handle retry attempts\r\n const retryAttemptMatch = action.match(/RETRY_ATTEMPT_(\\d+)_OF_(\\d+)_START/);\r\n if (retryAttemptMatch) {\r\n const [, current, total] = retryAttemptMatch;\r\n return `Retrying (${current}/${total})...`;\r\n }\r\n\r\n // Handle backup retry scheduling\r\n const backupRetryMatch = action.match(/BACKUP_RETRY_(\\d+)_OF_(\\d+)_SCHEDULED/);\r\n if (backupRetryMatch) {\r\n const [, current, total] = backupRetryMatch;\r\n return `Scheduling Retry (${parseInt(current)}/${total})...`;\r\n }\r\n\r\n // Handle replication retry scheduling\r\n const replicationRetryScheduledMatch = action.match(/REPLICATION_RETRY_(\\d+)_OF_(\\d+)_SCHEDULED/);\r\n if (replicationRetryScheduledMatch) {\r\n const [, current, total] = replicationRetryScheduledMatch;\r\n return `Retrying (${current}/${total})...`;\r\n }\r\n\r\n // Handle replication retry start\r\n const replicationRetryStartMatch = action.match(/REPLICATION_RETRY_(\\d+)_OF_(\\d+)_START/);\r\n if (replicationRetryStartMatch) {\r\n const [, current, total] = replicationRetryStartMatch;\r\n return `Retrying (${current}/${total})...`;\r\n }\r\n\r\n // Static action mappings\r\n const staticMessages: Record<string, string> = {\r\n INITIALIZE: 'Starting Backup...',\r\n PRE_BACKUP_START: 'Preparing Backup...',\r\n PRE_BACKUP_DRY_RUN_START: 'Performing Dry Run...',\r\n PRE_BACKUP_DRY_RUN_COMPLETE: 'Dry Run Complete',\r\n PRE_BACKUP_DRY_RUN_ERROR: 'Dry Run Encountered Errors',\r\n PRE_BACKUP_CHECKS_START: 'Running Checks...',\r\n PRE_BACKUP_CHECKS_COMPLETE: 'Checks Complete',\r\n PRE_BACKUP_SCRIPTS_START: 'Running Scripts...',\r\n PRE_BACKUP_SCRIPTS_COMPLETE: 'Scripts Complete',\r\n PRE_BACKUP_UNLOCK_STALE_LOCKS: 'Unlocking Repository...',\r\n PRE_BACKUP_COMPLETE: 'Pre-Backup Complete',\r\n BACKUP_OPERATION_START: 'Backing Up Files...',\r\n BACKUP_OPERATION_COMPLETE: 'Backup Complete',\r\n BACKUP_OPERATION_ERROR: 'Backup Encountered Errors',\r\n POST_BACKUP_START: 'Finalizing...',\r\n POST_BACKUP_SCRIPTS_START: 'Running Cleanup Scripts...',\r\n POST_BACKUP_SCRIPTS_COMPLETE: 'Cleanup Scripts Complete',\r\n POST_BACKUP_PRUNE_START: 'Pruning Old Backups...',\r\n POST_BACKUP_PRUNE_COMPLETE: 'Pruning Complete',\r\n POST_BACKUP_PRUNE_FAILED: 'Pruning Failed',\r\n POST_BACKUP_COMPLETE: 'Post-Backup Complete',\r\n POST_BACKUP_REPO_STATS_START: 'Updating Repository Statistics...',\r\n POST_BACKUP_REPO_STATS_COMPLETE: 'Repository Statistics Updated',\r\n POST_BACKUP_REPO_STATS_FAILED: 'Failed to Update Repository Statistics',\r\n TASK_COMPLETED: 'Completed Successfully',\r\n TASK_CANCELLED: 'Backup Cancelled by User.',\r\n TASK_FAILED: 'Backup Failed with Error.',\r\n FAILED_PERMANENTLY: 'Failed Permanently',\r\n ISO_CREATION_START: 'Creating ISO...',\r\n ISO_CREATION_COMPLETE: 'ISO Created Successfully',\r\n ISO_CREATION_FAILED: 'ISO Creation Failed',\r\n REMOTE_BACKUP_START: 'Backing Up Data...',\r\n REMOTE_BACKUP_COMPLETE: 'Data Backup Complete',\r\n REMOTE_BACKUP_FAILED: 'Data Backup Failed',\r\n ISO_ENCRYPTION_START: 'Encrypting ISO...',\r\n ISO_ENCRYPTION_COMPLETE: 'ISO Encryption Complete',\r\n ISO_UPLOAD_START: 'Uploading ISO...',\r\n ISO_UPLOAD_COMPLETE: 'ISO Upload Complete',\r\n ISO_UPLOAD_FAILED: 'ISO Upload Failed',\r\n BACKUP_WARNING: 'Hiccup Detected During Backup',\r\n // Replication actions\r\n REPLICATION_START: 'Replicating Snapshots...',\r\n REPLICATION_INIT_START: 'Initializing Replication Repository...',\r\n REPLICATION_INIT_COMPLETE: 'Replication Repository Initialized',\r\n REPLICATION_INIT_FAILED: 'Replication Repository Init Failed',\r\n REPLICATION_UNLOCK_START: 'Unlocking Replication Repository...',\r\n REPLICATION_UNLOCK_COMPLETE: 'Replication Repository Unlocked',\r\n REPLICATION_COPY_START: 'Copying Snapshot to Replication...',\r\n REPLICATION_COPY_COMPLETE: 'Snapshot Copied to Replication',\r\n REPLICATION_COPY_FAILED: 'Replication Copy Failed',\r\n REPLICATION_PRUNE_START: 'Pruning Replication Repository...',\r\n REPLICATION_PRUNE_COMPLETE: 'Replication Prune Complete',\r\n REPLICATION_PRUNE_FAILED: 'Replication Prune Failed',\r\n REPLICATION_STATS_START: 'Updating Replication Stats...',\r\n REPLICATION_STATS_COMPLETE: 'Replication Stats Updated',\r\n REPLICATION_COMPLETE: 'Replication Complete',\r\n REPLICATION_FAILED: 'Replication Failed',\r\n REPLICATION_PARTIAL_FAILURE: 'Some Replications Failed',\r\n REPLICATION_MANUAL_RETRY_START: 'Retrying Failed Replications...',\r\n REPLICATION_MANUAL_RETRY_COMPLETE: 'Replication Retry Complete',\r\n REPLICATION_MANUAL_RETRY_PARTIAL_FAILURE: 'Some Replication Retries Still Failed',\r\n };\r\n\r\n // Storage-name-aware replication action mappings\r\n const replicationMessagesWithStorage: Record<string, (name: string) => string> = {\r\n REPLICATION_START: (name) => `Replicating Snapshot to ${name}...`,\r\n REPLICATION_INIT_START: (name) => `Initializing Replication Repository in ${name}...`,\r\n REPLICATION_INIT_COMPLETE: (name) => `Replication Repository Initialized in ${name}`,\r\n REPLICATION_INIT_FAILED: (name) => `Replication Repository Init Failed in ${name}`,\r\n REPLICATION_UNLOCK_START: (name) => `Unlocking Replication Repository in ${name}...`,\r\n REPLICATION_UNLOCK_COMPLETE: (name) => `Replication Repository Unlocked in ${name}`,\r\n REPLICATION_COPY_START: (name) => `Copying Snapshot to ${name}...`,\r\n REPLICATION_COPY_COMPLETE: (name) => `Snapshot Copied to ${name}`,\r\n REPLICATION_COPY_FAILED: (name) => `Replication Copy Failed for ${name}`,\r\n REPLICATION_PRUNE_START: (name) => `Pruning Replication Repository in ${name}...`,\r\n REPLICATION_PRUNE_COMPLETE: (name) => `Replication Prune Complete for ${name}`,\r\n REPLICATION_PRUNE_FAILED: (name) => `Replication Prune Failed for ${name}`,\r\n REPLICATION_STATS_START: (name) => `Updating Replication Stats for ${name}...`,\r\n REPLICATION_STATS_COMPLETE: (name) => `Replication Stats Updated for ${name}`,\r\n REPLICATION_COMPLETE: (name) => `Replication Complete for ${name}`,\r\n REPLICATION_FAILED: (name) => `Replication Failed for ${name}`,\r\n };\r\n\r\n if (storageName && replicationMessagesWithStorage[action]) {\r\n return replicationMessagesWithStorage[action](storageName);\r\n }\r\n\r\n return staticMessages[action] || action;\r\n};\r\n\r\nexport function generateRestoreProgressMessage(progressData: BackupProgressData | null): string {\r\n if (!progressData?.events?.length) {\r\n return 'Initializing...';\r\n }\r\n\r\n // Find the last incomplete event or the most recent event\r\n const lastIncompleteEvent = progressData.events\r\n .slice()\r\n .reverse()\r\n .find((event) => !event.completed);\r\n\r\n const currentEvent = lastIncompleteEvent || progressData.events[progressData.events.length - 1];\r\n\r\n if (!currentEvent) {\r\n return 'Initializing...';\r\n }\r\n\r\n const { phase, action } = currentEvent;\r\n\r\n // Map phases to display names\r\n const phaseDisplayNames: Record<string, string> = {\r\n initializing: 'Initializing',\r\n 'pre-restore': 'Pre-Restore',\r\n restore: 'Restore',\r\n 'post-restore': 'Post-Restore',\r\n retry: 'Retrying',\r\n finished: 'Complete',\r\n };\r\n\r\n const phaseDisplay = phaseDisplayNames[phase] || phase;\r\n const actionDisplay = getRestoreEventActionMessage(action);\r\n\r\n if (phase === 'finished') {\r\n return actionDisplay;\r\n }\r\n\r\n return `${phaseDisplay}: ${actionDisplay}`;\r\n}\r\n\r\nexport const getRestoreEventActionMessage = (action: string): string => {\r\n // Handle script actions first\r\n const scriptMessage = handleScriptAction(action);\r\n if (scriptMessage) {\r\n return scriptMessage;\r\n }\r\n\r\n // Handle retry attempts\r\n const retryAttemptMatch = action.match(/RETRY_ATTEMPT_(\\d+)_OF_(\\d+)_START/);\r\n if (retryAttemptMatch) {\r\n const [, current, total] = retryAttemptMatch;\r\n return `Retrying (${current}/${total})...`;\r\n }\r\n\r\n // Handle backup retry scheduling\r\n const backupRetryMatch = action.match(/RESTORE_RETRY_(\\d+)_OF_(\\d+)_SCHEDULED/);\r\n if (backupRetryMatch) {\r\n const [, current, total] = backupRetryMatch;\r\n return `Scheduling Retry (${parseInt(current)}/${total})...`;\r\n }\r\n\r\n // Static action mappings\r\n const staticMessages: Record<string, string> = {\r\n INITIALIZE: 'Starting Restore...',\r\n PRE_RESTORE_START: 'Preparing Restore...',\r\n PRE_RESTORE_GET_SNAPSHOT: 'Getting Snapshot to Restore...',\r\n PRE_RESTORE_GET_SNAPSHOT_COMPLETE: 'Snapshot Retrieved',\r\n PRE_RESTORE_GET_SNAPSHOT_FAILED: 'Failed to Retrieve Snapshot',\r\n PRE_RESTORE_DRY_RUN_START: 'Performing Dry Run...',\r\n PRE_RESTORE_DRY_RUN_COMPLETE: 'Dry Run Complete',\r\n PRE_RESTORE_CHECKS_START: 'Running Checks...',\r\n PRE_RESTORE_CHECKS_COMPLETE: 'Checks Complete',\r\n PRE_RESTORE_UNLOCK_STALE_LOCKS: 'Unlocking Repository...',\r\n PRE_RESTORE_COMPLETE: 'Pre-Restore Complete',\r\n RESTORE_OPERATION_START: 'Restoring Files...',\r\n RESTORE_OPERATION_COMPLETE: 'Restore Complete',\r\n POST_RESTORE_START: 'Finalizing...',\r\n POST_RESTORE_COMPLETE: 'Post-Restore Complete',\r\n POST_RESTORE_REPO_STATS_START: 'Updating Repository Statistics...',\r\n POST_RESTORE_REPO_STATS_COMPLETE: 'Repository Statistics Updated',\r\n POST_RESTORE_WINDOWS_MOVE_START: 'Moving Restored files from temp directory to target path...',\r\n POST_RESTORE_WINDOWS_MOVE_ERROR: 'Failed to Move Restored files from temp directory to target path',\r\n POST_RESTORE_WINDOWS_MOVE_COMPLETE: 'Moved Restored files to target path',\r\n TASK_COMPLETED: 'Completed Successfully',\r\n TASK_FAILED: 'Restore Failed with Error.',\r\n TASK_CANCELLED: 'Restore Cancelled by User.',\r\n FAILED_PERMANENTLY: 'Failed Permanently',\r\n };\r\n return staticMessages[action] || action;\r\n};\r\n\r\nexport function extractResticData(progressData: BackupProgressData | null) {\r\n if (!progressData?.events?.length) {\r\n return null;\r\n }\r\n\r\n // Find the most recent event with resticData\r\n const eventWithResticData = progressData.events\r\n .slice()\r\n .reverse()\r\n .find((event) => event.resticData);\r\n\r\n return eventWithResticData?.resticData || null;\r\n}\r\n\r\n/**\r\n * Generates a human-readable progress message for a specific replication mirror.\r\n * Uses the mirror's latest event action and includes the storage name.\r\n */\r\nexport function generateMirrorProgressMessage(mirror: { storageName: string; events?: { action: string; completed: boolean }[] }): string {\r\n if (!mirror.events?.length) {\r\n return 'Pending...';\r\n }\r\n\r\n // Find the last incomplete event or the most recent event\r\n const lastIncompleteEvent = mirror.events\r\n .slice()\r\n .reverse()\r\n .find((event) => !event.completed);\r\n\r\n const currentEvent = lastIncompleteEvent || mirror.events[mirror.events.length - 1];\r\n\r\n if (!currentEvent) {\r\n return 'Pending...';\r\n }\r\n\r\n return getBackupEventActionMessage(currentEvent.action, mirror.storageName);\r\n}\r\n"],"names":["generateBackupProgressMessage","progressData","_a","currentEvent","event","phase","action","phaseDisplay","actionDisplay","getBackupEventActionMessage","handleScriptAction","scriptTypes","actionStates","scriptType","num","_","scriptMatch","scriptTypeKey","scriptNum","state","stateHandler","storageName","scriptMessage","retryAttemptMatch","current","total","backupRetryMatch","replicationRetryScheduledMatch","replicationRetryStartMatch","staticMessages","replicationMessagesWithStorage","name","generateRestoreProgressMessage","getRestoreEventActionMessage","extractResticData","eventWithResticData","generateMirrorProgressMessage","mirror"],"mappings":"AAEO,SAASA,EAA8BC,GAAiD;AAAxF,MAAAC;AACJ,MAAI,GAACA,IAAAD,KAAA,gBAAAA,EAAc,WAAd,QAAAC,EAAsB;AACxB,WAAO;AASV,QAAMC,IALsBF,EAAa,OACrC,MAAA,EACA,UACA,KAAK,CAACG,MAAU,CAACA,EAAM,SAAS,KAEQH,EAAa,OAAOA,EAAa,OAAO,SAAS,CAAC;AAE9F,MAAI,CAACE;AACF,WAAO;AAGV,QAAM,EAAE,OAAAE,GAAO,QAAAC,EAAA,IAAWH,GAYpBI,IAT4C;AAAA,IAC/C,cAAc;AAAA,IACd,cAAc;AAAA,IACd,QAAQ;AAAA,IACR,eAAe;AAAA,IACf,aAAa;AAAA,IACb,UAAU;AAAA,EAAA,EAG0BF,CAAK,KAAKA,GAC3CG,IAAgBC,EAA4BH,CAAM;AAExD,SAAID,MAAU,aACJG,IAGH,GAAGD,CAAY,KAAKC,CAAa;AAC3C;AAGA,MAAME,IAAqB,CAACJ,MAAkC;AAC3D,QAAMK,IAAc;AAAA,IACjB,eAAe;AAAA,IACf,kBAAkB;AAAA,IAClB,eAAe;AAAA,IACf,iBAAiB;AAAA,IACjB,aAAa;AAAA,EAAA,GAGVC,IAAe;AAAA,IAClB,OAAO,CAACC,GAAoBC,MAAgB,WAAWD,CAAU,IAAIC,CAAG;AAAA,IACxE,UAAU,CAACC,GAAWD,MAAgB,UAAUA,CAAG;AAAA,IACnD,MAAM,CAACC,GAAWD,MAAgB,UAAUA,CAAG;AAAA,IAC/C,OAAO,CAACD,GAAoBC,MAAgB,YAAYD,CAAU,IAAIC,CAAG;AAAA,EAAA,GAItEE,IAAcV,EAAO,MAAM,0DAA0D;AAE3F,MAAIU,GAAa;AACd,UAAM,GAAGC,GAAeC,GAAWC,CAAK,IAAIH,GACtCH,IAAaF,EAAYM,CAAyC,KAAK,UACvEG,IAAeR,EAAaO,CAAkC;AAEpE,QAAIC;AACD,aAAOA,EAAaP,GAAYK,CAAS;AAAA,EAE/C;AAEA,SAAO;AACV,GAGaT,IAA8B,CAACH,GAAgBe,MAAiC;AAE1F,QAAMC,IAAgBZ,EAAmBJ,CAAM;AAC/C,MAAIgB;AACD,WAAOA;AAIV,QAAMC,IAAoBjB,EAAO,MAAM,oCAAoC;AAC3E,MAAIiB,GAAmB;AACpB,UAAM,CAAA,EAAGC,GAASC,CAAK,IAAIF;AAC3B,WAAO,aAAaC,CAAO,IAAIC,CAAK;AAAA,EACvC;AAGA,QAAMC,IAAmBpB,EAAO,MAAM,uCAAuC;AAC7E,MAAIoB,GAAkB;AACnB,UAAM,CAAA,EAAGF,GAASC,CAAK,IAAIC;AAC3B,WAAO,qBAAqB,SAASF,CAAO,CAAC,IAAIC,CAAK;AAAA,EACzD;AAGA,QAAME,IAAiCrB,EAAO,MAAM,4CAA4C;AAChG,MAAIqB,GAAgC;AACjC,UAAM,CAAA,EAAGH,GAASC,CAAK,IAAIE;AAC3B,WAAO,aAAaH,CAAO,IAAIC,CAAK;AAAA,EACvC;AAGA,QAAMG,IAA6BtB,EAAO,MAAM,wCAAwC;AACxF,MAAIsB,GAA4B;AAC7B,UAAM,CAAA,EAAGJ,GAASC,CAAK,IAAIG;AAC3B,WAAO,aAAaJ,CAAO,IAAIC,CAAK;AAAA,EACvC;AAGA,QAAMI,IAAyC;AAAA,IAC5C,YAAY;AAAA,IACZ,kBAAkB;AAAA,IAClB,0BAA0B;AAAA,IAC1B,6BAA6B;AAAA,IAC7B,0BAA0B;AAAA,IAC1B,yBAAyB;AAAA,IACzB,4BAA4B;AAAA,IAC5B,0BAA0B;AAAA,IAC1B,6BAA6B;AAAA,IAC7B,+BAA+B;AAAA,IAC/B,qBAAqB;AAAA,IACrB,wBAAwB;AAAA,IACxB,2BAA2B;AAAA,IAC3B,wBAAwB;AAAA,IACxB,mBAAmB;AAAA,IACnB,2BAA2B;AAAA,IAC3B,8BAA8B;AAAA,IAC9B,yBAAyB;AAAA,IACzB,4BAA4B;AAAA,IAC5B,0BAA0B;AAAA,IAC1B,sBAAsB;AAAA,IACtB,8BAA8B;AAAA,IAC9B,iCAAiC;AAAA,IACjC,+BAA+B;AAAA,IAC/B,gBAAgB;AAAA,IAChB,gBAAgB;AAAA,IAChB,aAAa;AAAA,IACb,oBAAoB;AAAA,IACpB,oBAAoB;AAAA,IACpB,uBAAuB;AAAA,IACvB,qBAAqB;AAAA,IACrB,qBAAqB;AAAA,IACrB,wBAAwB;AAAA,IACxB,sBAAsB;AAAA,IACtB,sBAAsB;AAAA,IACtB,yBAAyB;AAAA,IACzB,kBAAkB;AAAA,IAClB,qBAAqB;AAAA,IACrB,mBAAmB;AAAA,IACnB,gBAAgB;AAAA;AAAA,IAEhB,mBAAmB;AAAA,IACnB,wBAAwB;AAAA,IACxB,2BAA2B;AAAA,IAC3B,yBAAyB;AAAA,IACzB,0BAA0B;AAAA,IAC1B,6BAA6B;AAAA,IAC7B,wBAAwB;AAAA,IACxB,2BAA2B;AAAA,IAC3B,yBAAyB;AAAA,IACzB,yBAAyB;AAAA,IACzB,4BAA4B;AAAA,IAC5B,0BAA0B;AAAA,IAC1B,yBAAyB;AAAA,IACzB,4BAA4B;AAAA,IAC5B,sBAAsB;AAAA,IACtB,oBAAoB;AAAA,IACpB,6BAA6B;AAAA,IAC7B,gCAAgC;AAAA,IAChC,mCAAmC;AAAA,IACnC,0CAA0C;AAAA,EAAA,GAIvCC,IAA2E;AAAA,IAC9E,mBAAmB,CAACC,MAAS,2BAA2BA,CAAI;AAAA,IAC5D,wBAAwB,CAACA,MAAS,0CAA0CA,CAAI;AAAA,IAChF,2BAA2B,CAACA,MAAS,yCAAyCA,CAAI;AAAA,IAClF,yBAAyB,CAACA,MAAS,yCAAyCA,CAAI;AAAA,IAChF,0BAA0B,CAACA,MAAS,uCAAuCA,CAAI;AAAA,IAC/E,6BAA6B,CAACA,MAAS,sCAAsCA,CAAI;AAAA,IACjF,wBAAwB,CAACA,MAAS,uBAAuBA,CAAI;AAAA,IAC7D,2BAA2B,CAACA,MAAS,sBAAsBA,CAAI;AAAA,IAC/D,yBAAyB,CAACA,MAAS,+BAA+BA,CAAI;AAAA,IACtE,yBAAyB,CAACA,MAAS,qCAAqCA,CAAI;AAAA,IAC5E,4BAA4B,CAACA,MAAS,kCAAkCA,CAAI;AAAA,IAC5E,0BAA0B,CAACA,MAAS,gCAAgCA,CAAI;AAAA,IACxE,yBAAyB,CAACA,MAAS,kCAAkCA,CAAI;AAAA,IACzE,4BAA4B,CAACA,MAAS,iCAAiCA,CAAI;AAAA,IAC3E,sBAAsB,CAACA,MAAS,4BAA4BA,CAAI;AAAA,IAChE,oBAAoB,CAACA,MAAS,0BAA0BA,CAAI;AAAA,EAAA;AAG/D,SAAIV,KAAeS,EAA+BxB,CAAM,IAC9CwB,EAA+BxB,CAAM,EAAEe,CAAW,IAGrDQ,EAAevB,CAAM,KAAKA;AACpC;AAEO,SAAS0B,EAA+B/B,GAAiD;AAxMzF,MAAAC;AAyMJ,MAAI,GAACA,IAAAD,KAAA,gBAAAA,EAAc,WAAd,QAAAC,EAAsB;AACxB,WAAO;AASV,QAAMC,IALsBF,EAAa,OACrC,MAAA,EACA,UACA,KAAK,CAACG,MAAU,CAACA,EAAM,SAAS,KAEQH,EAAa,OAAOA,EAAa,OAAO,SAAS,CAAC;AAE9F,MAAI,CAACE;AACF,WAAO;AAGV,QAAM,EAAE,OAAAE,GAAO,QAAAC,EAAA,IAAWH,GAYpBI,IAT4C;AAAA,IAC/C,cAAc;AAAA,IACd,eAAe;AAAA,IACf,SAAS;AAAA,IACT,gBAAgB;AAAA,IAChB,OAAO;AAAA,IACP,UAAU;AAAA,EAAA,EAG0BF,CAAK,KAAKA,GAC3CG,IAAgByB,EAA6B3B,CAAM;AAEzD,SAAID,MAAU,aACJG,IAGH,GAAGD,CAAY,KAAKC,CAAa;AAC3C;AAEO,MAAMyB,IAA+B,CAAC3B,MAA2B;AAErE,QAAMgB,IAAgBZ,EAAmBJ,CAAM;AAC/C,MAAIgB;AACD,WAAOA;AAIV,QAAMC,IAAoBjB,EAAO,MAAM,oCAAoC;AAC3E,MAAIiB,GAAmB;AACpB,UAAM,CAAA,EAAGC,GAASC,CAAK,IAAIF;AAC3B,WAAO,aAAaC,CAAO,IAAIC,CAAK;AAAA,EACvC;AAGA,QAAMC,IAAmBpB,EAAO,MAAM,wCAAwC;AAC9E,MAAIoB,GAAkB;AACnB,UAAM,CAAA,EAAGF,GAASC,CAAK,IAAIC;AAC3B,WAAO,qBAAqB,SAASF,CAAO,CAAC,IAAIC,CAAK;AAAA,EACzD;AA6BA,SA1B+C;AAAA,IAC5C,YAAY;AAAA,IACZ,mBAAmB;AAAA,IACnB,0BAA0B;AAAA,IAC1B,mCAAmC;AAAA,IACnC,iCAAiC;AAAA,IACjC,2BAA2B;AAAA,IAC3B,8BAA8B;AAAA,IAC9B,0BAA0B;AAAA,IAC1B,6BAA6B;AAAA,IAC7B,gCAAgC;AAAA,IAChC,sBAAsB;AAAA,IACtB,yBAAyB;AAAA,IACzB,4BAA4B;AAAA,IAC5B,oBAAoB;AAAA,IACpB,uBAAuB;AAAA,IACvB,+BAA+B;AAAA,IAC/B,kCAAkC;AAAA,IAClC,iCAAiC;AAAA,IACjC,iCAAiC;AAAA,IACjC,oCAAoC;AAAA,IACpC,gBAAgB;AAAA,IAChB,aAAa;AAAA,IACb,gBAAgB;AAAA,IAChB,oBAAoB;AAAA,EAAA,EAEDnB,CAAM,KAAKA;AACpC;AAEO,SAAS4B,EAAkBjC,GAAyC;AAlSpE,MAAAC;AAmSJ,MAAI,GAACA,IAAAD,KAAA,gBAAAA,EAAc,WAAd,QAAAC,EAAsB;AACxB,WAAO;AAIV,QAAMiC,IAAsBlC,EAAa,OACrC,MAAA,EACA,QAAA,EACA,KAAK,CAACG,MAAUA,EAAM,UAAU;AAEpC,UAAO+B,KAAA,gBAAAA,EAAqB,eAAc;AAC7C;AAMO,SAASC,EAA8BC,GAA4F;AApTnI,MAAAnC;AAqTJ,MAAI,GAACA,IAAAmC,EAAO,WAAP,QAAAnC,EAAe;AACjB,WAAO;AASV,QAAMC,IALsBkC,EAAO,OAC/B,MAAA,EACA,UACA,KAAK,CAACjC,MAAU,CAACA,EAAM,SAAS,KAEQiC,EAAO,OAAOA,EAAO,OAAO,SAAS,CAAC;AAElF,SAAKlC,IAIEM,EAA4BN,EAAa,QAAQkC,EAAO,WAAW,IAHhE;AAIb;"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@plutonhq/core-frontend",
3
3
  "description": "Pluton Core Frontend Library",
4
- "version": "0.1.29",
4
+ "version": "0.1.31",
5
5
  "author": "Plutonhq",
6
6
  "license": "Apache-2.0",
7
7
  "publishConfig": {
@@ -34,9 +34,7 @@ export interface Device {
34
34
  plans: PlanChildItem[];
35
35
  tags: string[];
36
36
  metrics: DeviceMetrics | null;
37
- settings: {
38
- tempDir: string;
39
- };
37
+ settings: DeviceSettings | null;
40
38
  }
41
39
 
42
40
  export interface DeviceMetrics {
@@ -195,7 +193,9 @@ export interface DeviceMetrics {
195
193
  }
196
194
 
197
195
  export interface DeviceSettings {
198
- tempDir?: string;
196
+ general?: {
197
+ tempDir?: string; // Temporary directory for both restic and rclone. If set, it overrides the individual tempDir settings for restic and rclone.
198
+ };
199
199
  restic?: {
200
200
  maxProcessor?: number | ''; // GOMAXPROCS
201
201
  cacheDir?: string; // RESTIC_CACHE_DIR
@@ -119,6 +119,7 @@ export type PlanScript = {
119
119
  logOutput: boolean;
120
120
  timeout?: number;
121
121
  abortOnError?: boolean;
122
+ runAsRoot?: boolean;
122
123
  };
123
124
 
124
125
  export interface PlanReplicationStorage {
@@ -4,18 +4,19 @@ import classes from './Footer.module.scss';
4
4
 
5
5
  interface FooterProps {
6
6
  version: string;
7
+ edition?: string;
7
8
  latestVersion?: string;
8
9
  hideUpgradeLink?: boolean;
9
10
  changeLogUrl?: string;
10
11
  }
11
12
 
12
- const Footer = ({ version = '1.0.0', latestVersion, hideUpgradeLink = false, changeLogUrl }: FooterProps) => {
13
+ const Footer = ({ version = '1.0.0', latestVersion, hideUpgradeLink = false, changeLogUrl, edition = '' }: FooterProps) => {
13
14
  const updateDocLink = getUpdateDocLink(hideUpgradeLink);
14
15
  return (
15
16
  <>
16
17
  <div className={classes.footer}>
17
18
  <span>
18
- Pluton v{version}{' '}
19
+ Pluton {edition} v{version}{' '}
19
20
  {latestVersion && (
20
21
  <span className={classes.newVersion} title={`Latest version: ${latestVersion}`}>
21
22
  <Icon type="arrow-up" size={12} />{' '}
@@ -107,8 +107,13 @@ const EditDevice = ({ close, device }: EditDeviceProps) => {
107
107
  <div className={classes.field}>
108
108
  <Input
109
109
  label="Temporary Working Directory"
110
- fieldValue={newDevice.settings?.tempDir || ''}
111
- onUpdate={(val) => setNewDevice({ ...newDevice, settings: { ...newDevice.settings, tempDir: val } })}
110
+ fieldValue={newDevice.settings?.general?.tempDir || ''}
111
+ onUpdate={(val) =>
112
+ setNewDevice({
113
+ ...newDevice,
114
+ settings: { ...newDevice.settings, general: { ...newDevice.settings?.general, tempDir: val } },
115
+ })
116
+ }
112
117
  full={true}
113
118
  hint="Where the backup, restores and downloads will be temporarily stored."
114
119
  />
@@ -142,9 +147,11 @@ const EditDevice = ({ close, device }: EditDeviceProps) => {
142
147
  <FolderPicker
143
148
  deviceId={device.id}
144
149
  title="Select Temporary Working Directory"
145
- selected={newDevice.settings?.tempDir || ''}
150
+ selected={newDevice.settings?.general?.tempDir || ''}
146
151
  close={() => setShowFileManager(false)}
147
- onSelect={(val) => setNewDevice({ ...newDevice, settings: { ...newDevice.settings, tempDir: val } })}
152
+ onSelect={(val) =>
153
+ setNewDevice({ ...newDevice, settings: { ...newDevice.settings, general: { ...newDevice.settings?.general, tempDir: val } } })
154
+ }
148
155
  />
149
156
  )}
150
157
  </SidePanel>
@@ -116,6 +116,9 @@
116
116
  color: #fff;
117
117
  }
118
118
  }
119
+ &.progressBarDryRun {
120
+ background-color: #b7cbf5;
121
+ }
119
122
  }
120
123
  }
121
124
  }
@@ -37,11 +37,13 @@ const BackupProgress = ({ item, sourceId, sourceType, planId, type = 'backup' }:
37
37
  const { data: progressData } =
38
38
  type === 'backup' ? useGetBackupProgress({ id, sourceId, sourceType, planId }) : useGetRestoreProgress({ id, sourceId, sourceType, planId });
39
39
 
40
- console.log('#### data :', progressData);
41
-
42
40
  const resticData = extractResticData(progressData);
43
41
  const progressMessage = type === 'backup' ? generateBackupProgressMessage(progressData) : generateRestoreProgressMessage(progressData);
44
42
 
43
+ // Check if DryRun progress tracking phase
44
+ const actualBackupStarted = progressData?.events.find((e: any) => e.action === 'BACKUP_OPERATION_START' && e.resticData);
45
+ const isDryRun = type === 'backup' && !actualBackupStarted;
46
+
45
47
  // Extract progress values from restic data or use defaults
46
48
  const {
47
49
  total_files_processed: total_files = resticData?.total_files || 0,
@@ -180,7 +182,7 @@ const BackupProgress = ({ item, sourceId, sourceType, planId, type = 'backup' }:
180
182
  </div>
181
183
  <div className={classes.progressBar}>
182
184
  <div
183
- className={`${classes.progressBarFill} ${progressPercent > 3 ? classes.progressBarFilled : ''}`}
185
+ className={`${classes.progressBarFill} ${progressPercent > 3 ? classes.progressBarFilled : ''} ${isDryRun ? classes.progressBarDryRun : ''}`}
184
186
  style={{ width: progressPercent + '%' }}
185
187
  >
186
188
  <span>{progressPercent}%</span>
@@ -273,7 +273,13 @@ const PlanForm = ({
273
273
  )}
274
274
  {step === 4 && (
275
275
  <div className={PFClasses.planStep}>
276
- <PlanAdvancedSettings plan={planSettings} appSettings={appSettings} onUpdate={onPlanSettingsChange} device={deviceInstance} />
276
+ <PlanAdvancedSettings
277
+ plan={planSettings}
278
+ appSettings={appSettings}
279
+ onUpdate={onPlanSettingsChange}
280
+ device={deviceInstance}
281
+ isEditing={type === 'edit'}
282
+ />
277
283
  </div>
278
284
  )}
279
285
  </div>
@@ -16,9 +16,10 @@ interface PlanAdvancedSettingsProps {
16
16
  appSettings?: Record<string, any>;
17
17
  device: Device;
18
18
  onUpdate: (notificationSettings: NewPlanSettings) => void;
19
+ isEditing: boolean;
19
20
  }
20
21
 
21
- const PlanAdvancedSettings = ({ plan, appSettings, device, onUpdate }: PlanAdvancedSettingsProps) => {
22
+ const PlanAdvancedSettings = ({ plan, appSettings, device, onUpdate, isEditing }: PlanAdvancedSettingsProps) => {
22
23
  const [advancedTab, setAdvancedTab] = useState('General');
23
24
  const settings = plan.settings;
24
25
  const integrationTypes = useMemo(() => {
@@ -75,7 +76,11 @@ const PlanAdvancedSettings = ({ plan, appSettings, device, onUpdate }: PlanAdvan
75
76
  )}
76
77
  <div className={classes.advancedTabContent}>
77
78
  {advancedTab === 'General' && (
78
- <PlanGeneralSettings settings={settings} onUpdate={(newSettings) => onUpdate({ ...plan, settings: newSettings })} />
79
+ <PlanGeneralSettings
80
+ settings={settings}
81
+ onUpdate={(newSettings) => onUpdate({ ...plan, settings: newSettings })}
82
+ isEditing={isEditing}
83
+ />
79
84
  )}
80
85
  {advancedTab === 'Notification' && (
81
86
  <PlanNotificationSettings
@@ -6,9 +6,10 @@ import classes from './PlanSettings.module.scss';
6
6
  interface PlanGeneralSettingsProps {
7
7
  settings: NewPlanSettings['settings'];
8
8
  onUpdate: (settings: NewPlanSettings['settings']) => void;
9
+ isEditing: boolean;
9
10
  }
10
11
 
11
- const PlanGeneralSettings = ({ settings, onUpdate }: PlanGeneralSettingsProps) => {
12
+ const PlanGeneralSettings = ({ settings, onUpdate, isEditing }: PlanGeneralSettingsProps) => {
12
13
  const { encryption, compression, retries, retryDelay } = settings;
13
14
  return (
14
15
  <>
@@ -16,6 +17,7 @@ const PlanGeneralSettings = ({ settings, onUpdate }: PlanGeneralSettingsProps) =
16
17
  <label className={classes.label}>Encryption</label>
17
18
  <Toggle
18
19
  fieldValue={encryption}
20
+ disabled={isEditing}
19
21
  onUpdate={(val: boolean) => onUpdate({ ...settings, encryption: val })}
20
22
  description="Encrypt Source Files before backup"
21
23
  />
@@ -1,6 +1,6 @@
1
1
  import classes from './PlanSettings.module.scss';
2
2
  import { useState } from 'react';
3
- import { NewPlanSettings } from '../../../@types/plans';
3
+ import { NewPlanSettings, PlanScript } from '../../../@types/plans';
4
4
  import Icon from '../../common/Icon/Icon';
5
5
  import Toggle from '../../common/form/Toggle/Toggle';
6
6
  import { secondsToMinutes } from '../../../utils/helpers';
@@ -45,7 +45,7 @@ const PlanScriptsSettings = ({
45
45
  onBackupFailure: [],
46
46
  onBackupComplete: [],
47
47
  },
48
- platform,
48
+ platform = '',
49
49
  onUpdate,
50
50
  }: PlanScriptsSettingsProps) => {
51
51
  const [showTimeoutSettings, setShowTimeoutSettings] = useState<string | false>(false);
@@ -56,8 +56,18 @@ const PlanScriptsSettings = ({
56
56
  onBackupFailure: false,
57
57
  onBackupComplete: false,
58
58
  });
59
- console.log('settings :', settings);
60
- console.log('### platform :', platform);
59
+
60
+ const showRootOption = platform.toLowerCase().includes('linux');
61
+ // const showRootOption = true;
62
+
63
+ const updateScriptBool = (eventKey: ScriptEventKey, index: number, key: keyof PlanScript, value: boolean) => {
64
+ const updatedScripts = [...(settings[eventKey] || [])];
65
+ updatedScripts[index] = {
66
+ ...updatedScripts[index],
67
+ [key]: value,
68
+ };
69
+ onUpdate({ ...settings, [eventKey]: updatedScripts });
70
+ };
61
71
 
62
72
  return (
63
73
  <div className={classes.eventTabs}>
@@ -171,48 +181,36 @@ const PlanScriptsSettings = ({
171
181
  // label="Enabled"
172
182
  fieldValue={script?.enabled || false}
173
183
  inline={true}
174
- onUpdate={(checked: boolean) => {
175
- const updatedScripts = [...(scripts || [])];
176
- updatedScripts[index] = {
177
- ...updatedScripts[index],
178
- enabled: checked,
179
- };
180
- onUpdate({ ...settings, [eventKey]: updatedScripts });
181
- }}
184
+ onUpdate={(checked: boolean) => updateScriptBool(key, index, 'enabled', checked)}
182
185
  />
183
186
  <span>Enabled</span>
184
187
  </div>
185
- <div className={classes.scriptOptionCheckbox}>
188
+ {/* <div className={classes.scriptOptionCheckbox}>
186
189
  <Toggle
187
190
  fieldValue={script?.logOutput || false}
188
191
  inline={true}
189
- onUpdate={(checked: boolean) => {
190
- const updatedScripts = [...(scripts || [])];
191
- updatedScripts[index] = {
192
- ...updatedScripts[index],
193
- logOutput: checked,
194
- };
195
- onUpdate({ ...settings, [eventKey]: updatedScripts });
196
- }}
192
+ onUpdate={(checked: boolean) => updateScriptBool(key, index, 'logOutput', checked)}
197
193
  />
198
194
  <span>Log Output</span>
199
- </div>
195
+ </div> */}
200
196
  <div className={classes.scriptOptionCheckbox}>
201
197
  <Toggle
202
- // label="Abort On Error"
203
198
  fieldValue={script?.abortOnError || false}
204
199
  inline={true}
205
- onUpdate={(checked: boolean) => {
206
- const updatedScripts = [...(scripts || [])];
207
- updatedScripts[index] = {
208
- ...updatedScripts[index],
209
- abortOnError: checked,
210
- };
211
- onUpdate({ ...settings, [eventKey]: updatedScripts });
212
- }}
200
+ onUpdate={(checked: boolean) => updateScriptBool(key, index, 'abortOnError', checked)}
213
201
  />
214
202
  <span>Abort on Error</span>
215
203
  </div>
204
+ {showRootOption && (
205
+ <div className={classes.scriptOptionCheckbox}>
206
+ <Toggle
207
+ fieldValue={script?.runAsRoot || false}
208
+ inline={true}
209
+ onUpdate={(checked: boolean) => updateScriptBool(key, index, 'runAsRoot', checked)}
210
+ />
211
+ <span>Run as Root</span>
212
+ </div>
213
+ )}
216
214
  <i className="pipe">|</i>
217
215
  <div
218
216
  title="Timeout Settings"
@@ -1,4 +1,4 @@
1
- import { useEffect } from 'react';
1
+ import { useEffect, useRef } from 'react';
2
2
  import classes from './PlanSettings.module.scss';
3
3
  import PathPicker from '../../common/PathPicker/PathPicker';
4
4
  import { NewPlanSettings } from '../../../@types/plans';
@@ -28,9 +28,16 @@ const PlanSourceSettings = ({ plan, onUpdate, error, isEditing }: PlanSourceSett
28
28
  );
29
29
  }
30
30
 
31
- // When the device changes, reset the sourceConfig paths to prevent invalid paths from being submitted
31
+ // When the device changes, reset the sourceConfig paths to prevent invalid paths from being submitted.
32
+ // Use a ref to track the previous deviceId so we only reset on an actual change (not on mount/remount,
33
+ // e.g. when navigating between steps in the Add Plan form).
34
+ const prevDeviceIdRef = useRef<string | null>(null);
32
35
  useEffect(() => {
33
- if (!isEditing) {
36
+ if (isEditing) {
37
+ prevDeviceIdRef.current = deviceId;
38
+ return;
39
+ }
40
+ if (prevDeviceIdRef.current !== null && prevDeviceIdRef.current !== deviceId) {
34
41
  onUpdate({
35
42
  ...plan,
36
43
  sourceConfig: {
@@ -39,6 +46,7 @@ const PlanSourceSettings = ({ plan, onUpdate, error, isEditing }: PlanSourceSett
39
46
  },
40
47
  });
41
48
  }
49
+ prevDeviceIdRef.current = deviceId;
42
50
  }, [isEditing, deviceId]);
43
51
 
44
52
  return (
@@ -1,4 +1,4 @@
1
- import { useEffect, useMemo, useState } from 'react';
1
+ import { useEffect, useMemo, useRef, useState } from 'react';
2
2
  import classes from './StoragePicker.module.scss';
3
3
  import Select from '../Select/Select';
4
4
  import { useGetStorages } from '../../../../services/storage';
@@ -70,11 +70,19 @@ const StoragePicker = ({ onUpdate, storagePath = '', storageId, disabled = false
70
70
  }
71
71
  }, [selectedStorage, path]);
72
72
 
73
+ // Reset the storage selection only when the device actually changes (not on mount/remount,
74
+ // e.g. when navigating between steps in the Add Plan form).
75
+ const prevDeviceIdRef = useRef<string | undefined | null>(null);
73
76
  useEffect(() => {
74
- if (!disabled) {
77
+ if (disabled) {
78
+ prevDeviceIdRef.current = deviceId;
79
+ return;
80
+ }
81
+ if (prevDeviceIdRef.current !== null && prevDeviceIdRef.current !== deviceId) {
75
82
  setSelectedStorage(null);
76
83
  setPath('');
77
84
  }
85
+ prevDeviceIdRef.current = deviceId;
78
86
  }, [deviceId, disabled]);
79
87
 
80
88
  // console.log('Storage path :', path, !disabled && isLocalStorage && !path);
@@ -61,6 +61,13 @@
61
61
  label:active:after {
62
62
  width: 30px;
63
63
  }
64
+ &.toggleDisabled {
65
+ opacity: 0.6;
66
+ cursor: not-allowed;
67
+ label {
68
+ cursor: not-allowed;
69
+ }
70
+ }
64
71
  }
65
72
 
66
73
  .description {
@@ -9,11 +9,22 @@ type ToggleProps = {
9
9
  inline?: boolean;
10
10
  hint?: string;
11
11
  error?: string;
12
+ disabled?: boolean;
12
13
  fieldValue: boolean;
13
14
  onUpdate: (f: boolean) => void;
14
15
  };
15
16
 
16
- const Toggle = ({ label, description, customClasses = '', fieldValue = false, inline = false, hint = '', error, onUpdate }: ToggleProps) => {
17
+ const Toggle = ({
18
+ label,
19
+ description,
20
+ customClasses = '',
21
+ fieldValue = false,
22
+ inline = false,
23
+ hint = '',
24
+ error,
25
+ disabled = false,
26
+ onUpdate,
27
+ }: ToggleProps) => {
17
28
  const toggleID = nanoid();
18
29
  const updateField = (event: React.FormEvent<HTMLInputElement>) => {
19
30
  const inputVal = event.currentTarget.value === 'true';
@@ -22,8 +33,8 @@ const Toggle = ({ label, description, customClasses = '', fieldValue = false, in
22
33
 
23
34
  return (
24
35
  <FormField type="toggle" label={label} hint={hint} error={error} inline={inline} classes={`${classes.toggleField} ${customClasses}`}>
25
- <div className={classes.toggleCheckbox}>
26
- <input type="checkbox" id={toggleID} value={fieldValue.toString()} onChange={updateField} checked={fieldValue} />
36
+ <div className={`${classes.toggleCheckbox} ${disabled ? classes.toggleDisabled : ''}`}>
37
+ <input type="checkbox" id={toggleID} value={fieldValue.toString()} onChange={updateField} checked={fieldValue} disabled={disabled} />
27
38
  <label htmlFor={toggleID}>{label}</label>
28
39
  {description && <span className={classes.description}>{description}</span>}
29
40
  </div>
@@ -99,13 +99,15 @@ export const usePlanSingleActions = (): {
99
99
 
100
100
  performBackupMutation.mutate(plan.id, {
101
101
  onSuccess: (data) => {
102
+ const msg = data?.message || `${isSync ? 'Sync' : 'Backup'} initiated successfully! 🚀`;
103
+ const notStarted = !isSync && data?.message && data?.message.includes('reached the concurrency limit');
102
104
  toast.update(toastId, {
103
- render: isSync ? data?.message || 'Sync initiated successfully! 🚀' : 'Backup initiated successfully! 🚀',
105
+ render: isSync ? msg : notStarted ? data?.message : 'Backup initiated successfully!',
104
106
  type: 'success',
105
107
  isLoading: false,
106
108
  autoClose: 3000,
107
109
  });
108
- if (!isSync) {
110
+ if (!isSync && !notStarted) {
109
111
  navigate(`/plan/${plan.id}?pendingbackup=1`);
110
112
  }
111
113
  },
@@ -7,6 +7,16 @@
7
7
  }
8
8
  }
9
9
 
10
+ .planError {
11
+ border: 1px solid #f3cfcf;
12
+ padding-top: 0;
13
+ cursor: pointer;
14
+ transition: all 0.12s linear;
15
+ &:hover {
16
+ background-color: #f3cfcf;
17
+ }
18
+ }
19
+
10
20
  .planActions {
11
21
  position: relative;
12
22
  .moreOptions {
@@ -65,7 +65,7 @@ const PlanSingle = () => {
65
65
  );
66
66
  }
67
67
 
68
- const { isActive, method, title, description, stats, settings } = plan;
68
+ const { isActive, method, title, description, verified, stats, settings } = plan;
69
69
  const prune = settings.prune;
70
70
  const snapshotsCount = stats.snapshots?.length || 0;
71
71
  const isSync = method === 'sync';
@@ -86,6 +86,11 @@ const PlanSingle = () => {
86
86
  <Icon size={14} type={'pause'} color="#bf8d20" /> Paused
87
87
  </span>
88
88
  )}
89
+ {verified?.hasError && (
90
+ <span className={`label error ${classes.planError}`} onClick={() => setShowIntegrityModal(true)}>
91
+ <Icon size={14} type={'error-circle-filled'} color="#dd6b6b" /> Integrity Error
92
+ </span>
93
+ )}
89
94
  </>
90
95
  }
91
96
  pageTitle={title}
@@ -114,6 +114,7 @@ export const getBackupEventActionMessage = (action: string, storageName?: string
114
114
  PRE_BACKUP_START: 'Preparing Backup...',
115
115
  PRE_BACKUP_DRY_RUN_START: 'Performing Dry Run...',
116
116
  PRE_BACKUP_DRY_RUN_COMPLETE: 'Dry Run Complete',
117
+ PRE_BACKUP_DRY_RUN_ERROR: 'Dry Run Encountered Errors',
117
118
  PRE_BACKUP_CHECKS_START: 'Running Checks...',
118
119
  PRE_BACKUP_CHECKS_COMPLETE: 'Checks Complete',
119
120
  PRE_BACKUP_SCRIPTS_START: 'Running Scripts...',