@plutonhq/core-frontend 0.1.30 → 0.1.32

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 (61) 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/BackupEvents/BackupEvents.js +33 -33
  13. package/dist-lib/components/Plan/BackupEvents/BackupEvents.js.map +1 -1
  14. package/dist-lib/components/Plan/BackupEvents/BackupEvents.module.scss.js +36 -36
  15. package/dist-lib/components/Plan/BackupProgress/BackupProgress.d.ts.map +1 -1
  16. package/dist-lib/components/Plan/BackupProgress/BackupProgress.js +79 -72
  17. package/dist-lib/components/Plan/BackupProgress/BackupProgress.js.map +1 -1
  18. package/dist-lib/components/Plan/BackupProgress/BackupProgress.module.scss.js +27 -25
  19. package/dist-lib/components/Plan/BackupProgress/BackupProgress.module.scss.js.map +1 -1
  20. package/dist-lib/components/Plan/Backups/Backups.d.ts.map +1 -1
  21. package/dist-lib/components/Plan/Backups/Backups.js +148 -144
  22. package/dist-lib/components/Plan/Backups/Backups.js.map +1 -1
  23. package/dist-lib/components/Plan/Backups/Backups.module.scss.js +34 -32
  24. package/dist-lib/components/Plan/Backups/Backups.module.scss.js.map +1 -1
  25. package/dist-lib/components/Plan/PlanSettings/PlanScriptsSettings.d.ts.map +1 -1
  26. package/dist-lib/components/Plan/PlanSettings/PlanScriptsSettings.js +89 -101
  27. package/dist-lib/components/Plan/PlanSettings/PlanScriptsSettings.js.map +1 -1
  28. package/dist-lib/components/Plan/PlanSettings/PlanSourceSettings.d.ts.map +1 -1
  29. package/dist-lib/components/Plan/PlanSettings/PlanSourceSettings.js +31 -25
  30. package/dist-lib/components/Plan/PlanSettings/PlanSourceSettings.js.map +1 -1
  31. package/dist-lib/components/common/form/StoragePicker/StoragePicker.d.ts.map +1 -1
  32. package/dist-lib/components/common/form/StoragePicker/StoragePicker.js +52 -46
  33. package/dist-lib/components/common/form/StoragePicker/StoragePicker.js.map +1 -1
  34. package/dist-lib/routes/PlanSingle/PlanSingle.d.ts.map +1 -1
  35. package/dist-lib/routes/PlanSingle/PlanSingle.js +76 -72
  36. package/dist-lib/routes/PlanSingle/PlanSingle.js.map +1 -1
  37. package/dist-lib/routes/PlanSingle/PlanSingle.module.scss.js +14 -12
  38. package/dist-lib/routes/PlanSingle/PlanSingle.module.scss.js.map +1 -1
  39. package/dist-lib/styles/core-frontend.css +1 -1
  40. package/dist-lib/styles/global.scss +4 -0
  41. package/dist-lib/utils/progressHelpers.d.ts.map +1 -1
  42. package/dist-lib/utils/progressHelpers.js +1 -0
  43. package/dist-lib/utils/progressHelpers.js.map +1 -1
  44. package/package.json +1 -1
  45. package/src/@types/devices.ts +4 -4
  46. package/src/@types/plans.ts +1 -0
  47. package/src/components/App/Footer/Footer.tsx +3 -2
  48. package/src/components/Device/EditDevice/EditDevice.tsx +11 -4
  49. package/src/components/Plan/BackupEvents/BackupEvents.module.scss +2 -0
  50. package/src/components/Plan/BackupEvents/BackupEvents.tsx +2 -2
  51. package/src/components/Plan/BackupProgress/BackupProgress.module.scss +4 -0
  52. package/src/components/Plan/BackupProgress/BackupProgress.tsx +11 -4
  53. package/src/components/Plan/Backups/Backups.module.scss +16 -0
  54. package/src/components/Plan/Backups/Backups.tsx +13 -2
  55. package/src/components/Plan/PlanSettings/PlanScriptsSettings.tsx +29 -31
  56. package/src/components/Plan/PlanSettings/PlanSourceSettings.tsx +11 -3
  57. package/src/components/common/form/StoragePicker/StoragePicker.tsx +10 -2
  58. package/src/routes/PlanSingle/PlanSingle.module.scss +10 -0
  59. package/src/routes/PlanSingle/PlanSingle.tsx +6 -1
  60. package/src/styles/global.scss +4 -0
  61. package/src/utils/progressHelpers.ts +1 -0
@@ -161,6 +161,10 @@ button {
161
161
  border-right: 1px solid var(--line-color);
162
162
  }
163
163
 
164
+ .linebreak-tooltip-content {
165
+ line-break: anywhere;
166
+ }
167
+
164
168
  .loadingScreen {
165
169
  width: 100%;
166
170
  height: 100vh;
@@ -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":"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"}
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.30",
4
+ "version": "0.1.32",
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>
@@ -259,4 +259,6 @@
259
259
  padding: 10px;
260
260
  word-break: break-word;
261
261
  color: var(--error-button-color);
262
+ max-height: 400px;
263
+ overflow: auto;
262
264
  }
@@ -154,8 +154,8 @@ const BackupEvents = ({ id, type = 'backup', sourceId, sourceType, planId, inPro
154
154
  )}
155
155
  </div>
156
156
  {showError && (
157
- <Modal title="Error Details" closeModal={() => setShowError(false)} width="400px">
158
- <div className={classes.errorDetails}>
157
+ <Modal title="Error Details" closeModal={() => setShowError(false)} width={'400px'}>
158
+ <div className={classes.errorDetails} data-length={showError.length}>
159
159
  {(() => {
160
160
  if (typeof showError === 'string') {
161
161
  if (showError.startsWith('main-')) {
@@ -40,6 +40,7 @@
40
40
  color: var(--error-button-color);
41
41
  margin-left: 0.5rem;
42
42
  font-size: 0.9em;
43
+ cursor: pointer;
43
44
  i {
44
45
  font-style: normal;
45
46
  }
@@ -116,6 +117,9 @@
116
117
  color: #fff;
117
118
  }
118
119
  }
120
+ &.progressBarDryRun {
121
+ background-color: #b7cbf5;
122
+ }
119
123
  }
120
124
  }
121
125
  }
@@ -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,
@@ -146,7 +148,12 @@ const BackupProgress = ({ item, sourceId, sourceType, planId, type = 'backup' }:
146
148
  {item.errorMsg && (
147
149
  <span className={classes.backupError}>
148
150
  <Icon type="error-circle-filled" size={13} />{' '}
149
- <i data-tooltip-id="htmlToolTip" data-tooltip-place="top" data-tooltip-html={item.errorMsg}>
151
+ <i
152
+ data-tooltip-id="htmlToolTip"
153
+ data-tooltip-place="top"
154
+ data-tooltip-html={`<div class="linebreak-tooltip-content">${item.errorMsg.slice(0, 120) + (item.errorMsg.length > 120 ? '...' : '')}</div>`}
155
+ onClick={() => setShowProgressDetails(true)}
156
+ >
150
157
  <u>Error</u> Occurred.
151
158
  </i>{' '}
152
159
  Retrying...
@@ -180,7 +187,7 @@ const BackupProgress = ({ item, sourceId, sourceType, planId, type = 'backup' }:
180
187
  </div>
181
188
  <div className={classes.progressBar}>
182
189
  <div
183
- className={`${classes.progressBarFill} ${progressPercent > 3 ? classes.progressBarFilled : ''}`}
190
+ className={`${classes.progressBarFill} ${progressPercent > 3 ? classes.progressBarFilled : ''} ${isDryRun ? classes.progressBarDryRun : ''}`}
184
191
  style={{ width: progressPercent + '%' }}
185
192
  >
186
193
  <span>{progressPercent}%</span>
@@ -233,6 +233,22 @@
233
233
  }
234
234
  }
235
235
 
236
+ .errorDetailsModal {
237
+ width: 100%;
238
+ max-height: 500px;
239
+ overflow: auto;
240
+ font-family: monospace;
241
+ box-sizing: border-box;
242
+ border-radius: 6px;
243
+ background-color: var(--primary-color-lighter);
244
+ p {
245
+ border-bottom: 1px solid;
246
+ padding: 12px;
247
+ margin: 0;
248
+ border-color: var(--line-color);
249
+ }
250
+ }
251
+
236
252
  @media only screen and (max-width: 768px) {
237
253
  .backupsTable {
238
254
  overflow: auto;
@@ -21,7 +21,7 @@ import BackupEvents from '../BackupEvents/BackupEvents';
21
21
  import Input from '../../common/form/Input/Input';
22
22
  import MirrorStatusBadge from '../Mirrors/MirrorStatusBadge';
23
23
  import MirrorStorageSelectorModal from '../Mirrors/MirrorStorageSelectorModal';
24
- import { SidePanel, SnapshotViewer } from '../..';
24
+ import { Modal, SidePanel, SnapshotViewer } from '../..';
25
25
 
26
26
  const DownloadLabel = ({ download, downloadBackup }: { download: Backup['download']; downloadBackup: () => void }) => {
27
27
  if (download?.status === 'started') {
@@ -101,6 +101,7 @@ const Backups = ({
101
101
  const [showRestoreModal, setShowRestoreModal] = useState<Backup | false>(false);
102
102
  const [showBackupEvents, setShowBackupEvents] = useState<false | string>(false);
103
103
  const [showSnapshotViewer, setShowSnapshotViewer] = useState<Backup | false>(false);
104
+ const [showBackupError, setShowBackupError] = useState<string | false>(false);
104
105
  const [showEditModal, setShowEditModal] = useState<Backup | false>(false);
105
106
  const [showStorageSelector, setShowStorageSelector] = useState<Backup | false>(false);
106
107
  const queryClient = useQueryClient();
@@ -240,8 +241,9 @@ const Backups = ({
240
241
  <div
241
242
  className={`${classes.status} ${errorMsg ? classes.statusHasError : ''}`}
242
243
  data-tooltip-id="htmlToolTip"
243
- data-tooltip-html={`<div><string>Error</string>: ${errorMsg}</div>`}
244
+ data-tooltip-html={`<div class="linebreak-tooltip-content"><string>Error</string>: ${errorMsg?.slice(0, 120) + (errorMsg && errorMsg.length > 120 ? '...' : '')}</div>`}
244
245
  data-tooltip-hidden={!errorMsg}
246
+ onClick={() => errorMsg && errorMsg.length > 120 && setShowBackupError(errorMsg)}
245
247
  >
246
248
  <StatusLabel status={status} hasError={!!errorMsg} />
247
249
  </div>
@@ -453,6 +455,15 @@ const Backups = ({
453
455
  />
454
456
  </SidePanel>
455
457
  )}
458
+ {showBackupError && (
459
+ <Modal title="Error Details" closeModal={() => setShowBackupError(false)} width={'600px'}>
460
+ <div className={`${classes.errorDetailsModal} styled__scrollbar`}>
461
+ {showBackupError.split('\n').map((line, index) => (
462
+ <p key={index}>{line}</p>
463
+ ))}
464
+ </div>
465
+ </Modal>
466
+ )}
456
467
  </div>
457
468
  );
458
469
  };
@@ -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);
@@ -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}
@@ -161,6 +161,10 @@ button {
161
161
  border-right: 1px solid var(--line-color);
162
162
  }
163
163
 
164
+ .linebreak-tooltip-content {
165
+ line-break: anywhere;
166
+ }
167
+
164
168
  .loadingScreen {
165
169
  width: 100%;
166
170
  height: 100vh;
@@ -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...',