@plutonhq/core-frontend 0.1.13 → 0.1.15

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 (217) hide show
  1. package/dist-lib/@types/backups.d.ts +26 -0
  2. package/dist-lib/@types/backups.d.ts.map +1 -1
  3. package/dist-lib/@types/devices.d.ts +7 -0
  4. package/dist-lib/@types/devices.d.ts.map +1 -1
  5. package/dist-lib/@types/plans.d.ts +21 -1
  6. package/dist-lib/@types/plans.d.ts.map +1 -1
  7. package/dist-lib/@types/restores.d.ts +2 -0
  8. package/dist-lib/@types/restores.d.ts.map +1 -1
  9. package/dist-lib/components/Device/DeviceBackups/DeviceBackups.d.ts +3 -2
  10. package/dist-lib/components/Device/DeviceBackups/DeviceBackups.d.ts.map +1 -1
  11. package/dist-lib/components/Device/DeviceBackups/DeviceBackups.js +73 -85
  12. package/dist-lib/components/Device/DeviceBackups/DeviceBackups.js.map +1 -1
  13. package/dist-lib/components/Plan/BackupEvents/BackupEvents.d.ts.map +1 -1
  14. package/dist-lib/components/Plan/BackupEvents/BackupEvents.js +88 -50
  15. package/dist-lib/components/Plan/BackupEvents/BackupEvents.js.map +1 -1
  16. package/dist-lib/components/Plan/BackupEvents/BackupEvents.module.scss.js +70 -38
  17. package/dist-lib/components/Plan/BackupEvents/BackupEvents.module.scss.js.map +1 -1
  18. package/dist-lib/components/Plan/BackupProgress/BackupProgress.d.ts.map +1 -1
  19. package/dist-lib/components/Plan/BackupProgress/BackupProgress.js +166 -123
  20. package/dist-lib/components/Plan/BackupProgress/BackupProgress.js.map +1 -1
  21. package/dist-lib/components/Plan/BackupProgress/BackupProgress.module.scss.js +64 -30
  22. package/dist-lib/components/Plan/BackupProgress/BackupProgress.module.scss.js.map +1 -1
  23. package/dist-lib/components/Plan/Backups/Backups.d.ts +8 -1
  24. package/dist-lib/components/Plan/Backups/Backups.d.ts.map +1 -1
  25. package/dist-lib/components/Plan/Backups/Backups.js +154 -125
  26. package/dist-lib/components/Plan/Backups/Backups.js.map +1 -1
  27. package/dist-lib/components/Plan/EditPlan/EditPlan.d.ts.map +1 -1
  28. package/dist-lib/components/Plan/EditPlan/EditPlan.js +11 -10
  29. package/dist-lib/components/Plan/EditPlan/EditPlan.js.map +1 -1
  30. package/dist-lib/components/Plan/Mirrors/MirrorDetails.d.ts +12 -0
  31. package/dist-lib/components/Plan/Mirrors/MirrorDetails.d.ts.map +1 -0
  32. package/dist-lib/components/Plan/Mirrors/MirrorDetails.js +68 -0
  33. package/dist-lib/components/Plan/Mirrors/MirrorDetails.js.map +1 -0
  34. package/dist-lib/components/Plan/Mirrors/MirrorDetails.module.scss.js +26 -0
  35. package/dist-lib/components/Plan/Mirrors/MirrorDetails.module.scss.js.map +1 -0
  36. package/dist-lib/components/Plan/Mirrors/MirrorStatusBadge.d.ts +11 -0
  37. package/dist-lib/components/Plan/Mirrors/MirrorStatusBadge.d.ts.map +1 -0
  38. package/dist-lib/components/Plan/Mirrors/MirrorStatusBadge.js +38 -0
  39. package/dist-lib/components/Plan/Mirrors/MirrorStatusBadge.js.map +1 -0
  40. package/dist-lib/components/Plan/Mirrors/MirrorStatusBadge.module.scss.js +16 -0
  41. package/dist-lib/components/Plan/Mirrors/MirrorStatusBadge.module.scss.js.map +1 -0
  42. package/dist-lib/components/Plan/Mirrors/MirrorStorageSelector.d.ts +14 -0
  43. package/dist-lib/components/Plan/Mirrors/MirrorStorageSelector.d.ts.map +1 -0
  44. package/dist-lib/components/Plan/Mirrors/MirrorStorageSelector.js +54 -0
  45. package/dist-lib/components/Plan/Mirrors/MirrorStorageSelector.js.map +1 -0
  46. package/dist-lib/components/Plan/Mirrors/MirrorStorageSelector.module.scss.js +26 -0
  47. package/dist-lib/components/Plan/Mirrors/MirrorStorageSelector.module.scss.js.map +1 -0
  48. package/dist-lib/components/Plan/Mirrors/MirrorStorageSelectorModal.d.ts +15 -0
  49. package/dist-lib/components/Plan/Mirrors/MirrorStorageSelectorModal.d.ts.map +1 -0
  50. package/dist-lib/components/Plan/Mirrors/MirrorStorageSelectorModal.js +34 -0
  51. package/dist-lib/components/Plan/Mirrors/MirrorStorageSelectorModal.js.map +1 -0
  52. package/dist-lib/components/Plan/PlanBackups/PlanBackups.d.ts.map +1 -1
  53. package/dist-lib/components/Plan/PlanBackups/PlanBackups.js +20 -17
  54. package/dist-lib/components/Plan/PlanBackups/PlanBackups.js.map +1 -1
  55. package/dist-lib/components/Plan/PlanForm/PlanForm.d.ts +2 -1
  56. package/dist-lib/components/Plan/PlanForm/PlanForm.d.ts.map +1 -1
  57. package/dist-lib/components/Plan/PlanForm/PlanForm.js +121 -94
  58. package/dist-lib/components/Plan/PlanForm/PlanForm.js.map +1 -1
  59. package/dist-lib/components/Plan/PlanIntegrity/PlanIntegrity.d.ts +16 -0
  60. package/dist-lib/components/Plan/PlanIntegrity/PlanIntegrity.d.ts.map +1 -0
  61. package/dist-lib/components/Plan/PlanIntegrity/PlanIntegrity.js +115 -0
  62. package/dist-lib/components/Plan/PlanIntegrity/PlanIntegrity.js.map +1 -0
  63. package/dist-lib/components/Plan/PlanIntegrity/PlanIntegrity.module.scss.js +26 -0
  64. package/dist-lib/components/Plan/PlanIntegrity/PlanIntegrity.module.scss.js.map +1 -0
  65. package/dist-lib/components/Plan/PlanItems/PlanItem.d.ts.map +1 -1
  66. package/dist-lib/components/Plan/PlanItems/PlanItem.js +58 -59
  67. package/dist-lib/components/Plan/PlanItems/PlanItem.js.map +1 -1
  68. package/dist-lib/components/Plan/PlanPendingBackup/PlanPendingBackup.d.ts +2 -2
  69. package/dist-lib/components/Plan/PlanPendingBackup/PlanPendingBackup.d.ts.map +1 -1
  70. package/dist-lib/components/Plan/PlanPendingBackup/PlanPendingBackup.js.map +1 -1
  71. package/dist-lib/components/Plan/PlanRemoveModal/PlanRemoveModal.js +8 -8
  72. package/dist-lib/components/Plan/PlanRemoveModal/PlanRemoveModal.js.map +1 -1
  73. package/dist-lib/components/Plan/PlanSettings/PlanReplicationSettings.d.ts +14 -0
  74. package/dist-lib/components/Plan/PlanSettings/PlanReplicationSettings.d.ts.map +1 -0
  75. package/dist-lib/components/Plan/PlanSettings/PlanReplicationSettings.js +290 -0
  76. package/dist-lib/components/Plan/PlanSettings/PlanReplicationSettings.js.map +1 -0
  77. package/dist-lib/components/Plan/PlanSettings/PlanReplicationSettings.module.scss.js +26 -0
  78. package/dist-lib/components/Plan/PlanSettings/PlanReplicationSettings.module.scss.js.map +1 -0
  79. package/dist-lib/components/Plan/PlanStats/PlanStats.d.ts.map +1 -1
  80. package/dist-lib/components/Plan/PlanStats/PlanStats.js +41 -42
  81. package/dist-lib/components/Plan/PlanStats/PlanStats.js.map +1 -1
  82. package/dist-lib/components/Plan/PlanStats/PlanStats.module.scss.js +5 -5
  83. package/dist-lib/components/Plan/PlanStorageInfo/PlanStorageInfo.d.ts +15 -0
  84. package/dist-lib/components/Plan/PlanStorageInfo/PlanStorageInfo.d.ts.map +1 -0
  85. package/dist-lib/components/Plan/PlanStorageInfo/PlanStorageInfo.js +69 -0
  86. package/dist-lib/components/Plan/PlanStorageInfo/PlanStorageInfo.js.map +1 -0
  87. package/dist-lib/components/Plan/PlanStorageInfo/PlanStorageInfo.module.scss.js +16 -0
  88. package/dist-lib/components/Plan/PlanStorageInfo/PlanStorageInfo.module.scss.js.map +1 -0
  89. package/dist-lib/components/Restore/RestoreWizard/RestoreConfirmStep.d.ts.map +1 -1
  90. package/dist-lib/components/Restore/RestoreWizard/RestoreConfirmStep.js +36 -34
  91. package/dist-lib/components/Restore/RestoreWizard/RestoreConfirmStep.js.map +1 -1
  92. package/dist-lib/components/Restore/RestoreWizard/RestorePreviewStep.d.ts.map +1 -1
  93. package/dist-lib/components/Restore/RestoreWizard/RestorePreviewStep.js +7 -5
  94. package/dist-lib/components/Restore/RestoreWizard/RestorePreviewStep.js.map +1 -1
  95. package/dist-lib/components/Restore/RestoreWizard/RestoreSettingsStep.d.ts +12 -4
  96. package/dist-lib/components/Restore/RestoreWizard/RestoreSettingsStep.d.ts.map +1 -1
  97. package/dist-lib/components/Restore/RestoreWizard/RestoreSettingsStep.js +44 -32
  98. package/dist-lib/components/Restore/RestoreWizard/RestoreSettingsStep.js.map +1 -1
  99. package/dist-lib/components/Restore/RestoreWizard/RestoreWizard.d.ts +5 -1
  100. package/dist-lib/components/Restore/RestoreWizard/RestoreWizard.d.ts.map +1 -1
  101. package/dist-lib/components/Restore/RestoreWizard/RestoreWizard.js +48 -44
  102. package/dist-lib/components/Restore/RestoreWizard/RestoreWizard.js.map +1 -1
  103. package/dist-lib/components/Restore/RestoreWizard/RestoreWizard.module.scss.js +32 -32
  104. package/dist-lib/components/Settings/GeneralSettings/GeneralSettings.d.ts +1 -1
  105. package/dist-lib/components/Settings/GeneralSettings/GeneralSettings.d.ts.map +1 -1
  106. package/dist-lib/components/Settings/GeneralSettings/GeneralSettings.js +52 -24
  107. package/dist-lib/components/Settings/GeneralSettings/GeneralSettings.js.map +1 -1
  108. package/dist-lib/components/Settings/IntegrationSettings/IntegrationSettings.d.ts.map +1 -1
  109. package/dist-lib/components/Settings/IntegrationSettings/IntegrationSettings.js +28 -19
  110. package/dist-lib/components/Settings/IntegrationSettings/IntegrationSettings.js.map +1 -1
  111. package/dist-lib/components/Settings/TwoFactorSetup/TwoFactorSetup.d.ts +7 -0
  112. package/dist-lib/components/Settings/TwoFactorSetup/TwoFactorSetup.d.ts.map +1 -0
  113. package/dist-lib/components/Settings/TwoFactorSetup/TwoFactorSetup.js +79 -0
  114. package/dist-lib/components/Settings/TwoFactorSetup/TwoFactorSetup.js.map +1 -0
  115. package/dist-lib/components/Settings/TwoFactorSetup/TwoFactorSetup.module.scss.js +24 -0
  116. package/dist-lib/components/Settings/TwoFactorSetup/TwoFactorSetup.module.scss.js.map +1 -0
  117. package/dist-lib/components/common/Icon/Icon.d.ts.map +1 -1
  118. package/dist-lib/components/common/Icon/Icon.js +11 -0
  119. package/dist-lib/components/common/Icon/Icon.js.map +1 -1
  120. package/dist-lib/components/common/PageHeader/PageHeader.module.scss.js +6 -6
  121. package/dist-lib/components/index.d.ts +6 -0
  122. package/dist-lib/components/index.d.ts.map +1 -1
  123. package/dist-lib/components.js +102 -90
  124. package/dist-lib/components.js.map +1 -1
  125. package/dist-lib/hooks/usePwaAutoUpdate.d.ts +11 -2
  126. package/dist-lib/hooks/usePwaAutoUpdate.d.ts.map +1 -1
  127. package/dist-lib/hooks/usePwaAutoUpdate.js +32 -10
  128. package/dist-lib/hooks/usePwaAutoUpdate.js.map +1 -1
  129. package/dist-lib/router.d.ts.map +1 -1
  130. package/dist-lib/router.js +46 -35
  131. package/dist-lib/router.js.map +1 -1
  132. package/dist-lib/routes/DeviceSingle/DeviceSingle.d.ts.map +1 -1
  133. package/dist-lib/routes/DeviceSingle/DeviceSingle.js +40 -40
  134. package/dist-lib/routes/DeviceSingle/DeviceSingle.js.map +1 -1
  135. package/dist-lib/routes/PlanSingle/PlanSingle.d.ts.map +1 -1
  136. package/dist-lib/routes/PlanSingle/PlanSingle.js +123 -98
  137. package/dist-lib/routes/PlanSingle/PlanSingle.js.map +1 -1
  138. package/dist-lib/services/backups.d.ts +15 -2
  139. package/dist-lib/services/backups.d.ts.map +1 -1
  140. package/dist-lib/services/backups.js +119 -100
  141. package/dist-lib/services/backups.js.map +1 -1
  142. package/dist-lib/services/plans.d.ts +20 -0
  143. package/dist-lib/services/plans.d.ts.map +1 -1
  144. package/dist-lib/services/plans.js +227 -172
  145. package/dist-lib/services/plans.js.map +1 -1
  146. package/dist-lib/services/restores.d.ts +10 -2
  147. package/dist-lib/services/restores.d.ts.map +1 -1
  148. package/dist-lib/services/restores.js +61 -57
  149. package/dist-lib/services/restores.js.map +1 -1
  150. package/dist-lib/services/settings.d.ts +16 -0
  151. package/dist-lib/services/settings.d.ts.map +1 -1
  152. package/dist-lib/services/settings.js +147 -68
  153. package/dist-lib/services/settings.js.map +1 -1
  154. package/dist-lib/services/users.d.ts.map +1 -1
  155. package/dist-lib/services/users.js +32 -32
  156. package/dist-lib/services/users.js.map +1 -1
  157. package/dist-lib/services.js +113 -101
  158. package/dist-lib/styles/core-frontend.css +1 -1
  159. package/dist-lib/utils/progressHelpers.d.ts +12 -1
  160. package/dist-lib/utils/progressHelpers.d.ts.map +1 -1
  161. package/dist-lib/utils/progressHelpers.js +121 -63
  162. package/dist-lib/utils/progressHelpers.js.map +1 -1
  163. package/dist-lib/utils.js +29 -28
  164. package/package.json +1 -1
  165. package/src/@types/backups.ts +28 -0
  166. package/src/@types/devices.ts +8 -0
  167. package/src/@types/plans.ts +23 -1
  168. package/src/@types/restores.ts +2 -0
  169. package/src/components/Device/DeviceBackups/DeviceBackups.tsx +11 -36
  170. package/src/components/Plan/BackupEvents/BackupEvents.module.scss +65 -0
  171. package/src/components/Plan/BackupEvents/BackupEvents.tsx +65 -4
  172. package/src/components/Plan/BackupProgress/BackupProgress.module.scss +121 -3
  173. package/src/components/Plan/BackupProgress/BackupProgress.tsx +149 -71
  174. package/src/components/Plan/Backups/Backups.tsx +52 -4
  175. package/src/components/Plan/EditPlan/EditPlan.tsx +1 -0
  176. package/src/components/Plan/Mirrors/MirrorDetails.module.scss +76 -0
  177. package/src/components/Plan/Mirrors/MirrorDetails.tsx +100 -0
  178. package/src/components/Plan/Mirrors/MirrorStatusBadge.module.scss +25 -0
  179. package/src/components/Plan/Mirrors/MirrorStatusBadge.tsx +65 -0
  180. package/src/components/Plan/Mirrors/MirrorStorageSelector.module.scss +97 -0
  181. package/src/components/Plan/Mirrors/MirrorStorageSelector.tsx +70 -0
  182. package/src/components/Plan/Mirrors/MirrorStorageSelectorModal.tsx +40 -0
  183. package/src/components/Plan/PlanBackups/PlanBackups.tsx +4 -1
  184. package/src/components/Plan/PlanForm/PlanForm.tsx +44 -17
  185. package/src/components/Plan/PlanIntegrity/PlanIntegrity.module.scss +110 -0
  186. package/src/components/Plan/PlanIntegrity/PlanIntegrity.tsx +187 -0
  187. package/src/components/Plan/PlanItems/PlanItem.tsx +3 -3
  188. package/src/components/Plan/PlanPendingBackup/PlanPendingBackup.tsx +2 -2
  189. package/src/components/Plan/PlanRemoveModal/PlanRemoveModal.tsx +1 -1
  190. package/src/components/Plan/PlanSettings/PlanReplicationSettings.module.scss +105 -0
  191. package/src/components/Plan/PlanSettings/PlanReplicationSettings.tsx +334 -0
  192. package/src/components/Plan/PlanStats/PlanStats.module.scss +1 -1
  193. package/src/components/Plan/PlanStats/PlanStats.tsx +8 -8
  194. package/src/components/Plan/PlanStorageInfo/PlanStorageInfo.module.scss +43 -0
  195. package/src/components/Plan/PlanStorageInfo/PlanStorageInfo.tsx +83 -0
  196. package/src/components/Restore/RestoreWizard/RestoreConfirmStep.tsx +2 -0
  197. package/src/components/Restore/RestoreWizard/RestorePreviewStep.tsx +2 -0
  198. package/src/components/Restore/RestoreWizard/RestoreSettingsStep.tsx +36 -13
  199. package/src/components/Restore/RestoreWizard/RestoreWizard.module.scss +4 -0
  200. package/src/components/Restore/RestoreWizard/RestoreWizard.tsx +9 -1
  201. package/src/components/Settings/GeneralSettings/GeneralSettings.tsx +38 -2
  202. package/src/components/Settings/IntegrationSettings/IntegrationSettings.tsx +9 -2
  203. package/src/components/Settings/TwoFactorSetup/TwoFactorSetup.module.scss +62 -0
  204. package/src/components/Settings/TwoFactorSetup/TwoFactorSetup.tsx +102 -0
  205. package/src/components/common/Icon/Icon.tsx +10 -1
  206. package/src/components/common/PageHeader/PageHeader.module.scss +3 -0
  207. package/src/components/index.ts +8 -0
  208. package/src/hooks/usePwaAutoUpdate.ts +51 -11
  209. package/src/router.tsx +26 -17
  210. package/src/routes/DeviceSingle/DeviceSingle.tsx +3 -3
  211. package/src/routes/PlanSingle/PlanSingle.tsx +21 -0
  212. package/src/services/backups.ts +32 -9
  213. package/src/services/plans.ts +75 -0
  214. package/src/services/restores.ts +10 -2
  215. package/src/services/settings.ts +90 -0
  216. package/src/services/users.ts +14 -5
  217. package/src/utils/progressHelpers.ts +85 -1
@@ -1,41 +1,15 @@
1
- import { useMemo } from 'react';
2
1
  import { NavLink } from 'react-router';
3
- import { DevicePlan } from '../../../@types/devices';
2
+ import { DevicePlan, DeviceStorage } from '../../../@types/devices';
4
3
  import Icon from '../../common/Icon/Icon';
5
4
  import classes from './DeviceBackups.module.scss';
6
5
  import { formatBytes } from '../../../utils/helpers';
7
6
 
8
7
  interface DeviceBackupsProps {
9
8
  plans: DevicePlan[];
9
+ storages: DeviceStorage[];
10
10
  }
11
11
 
12
- const DeviceBackups = ({ plans }: DeviceBackupsProps) => {
13
- const plansStorages = useMemo(() => {
14
- const storages: (DevicePlan['storage'] & { size: number })[] = [];
15
-
16
- //first calculate each storage's size from the plan size.
17
- const storageSizes: { [key: string]: number } = {};
18
- plans.forEach((plan) => {
19
- const planStorageId = plan.storage.id;
20
- if (!storageSizes[planStorageId]) {
21
- storageSizes[planStorageId] = 0;
22
- }
23
- storageSizes[planStorageId] += plan.size;
24
- });
25
-
26
- plans.forEach((plan) => {
27
- if (!storages.some((storage) => storage.id === plan.storage.id)) {
28
- // if (plan.storage.id !== 'local') {
29
- // TODO: implement this check
30
- storages.push({ ...plan.storage, size: storageSizes[plan.storage.id] });
31
- // }
32
- }
33
- });
34
- return storages;
35
- }, [plans]);
36
-
37
- console.log('plansStorages :', plansStorages);
38
-
12
+ const DeviceBackups = ({ plans, storages }: DeviceBackupsProps) => {
39
13
  return (
40
14
  <div className={classes.devicePlans}>
41
15
  {/* Backup Plans */}
@@ -46,7 +20,7 @@ const DeviceBackups = ({ plans }: DeviceBackupsProps) => {
46
20
  <div className={classes.widgetContent}>
47
21
  {plans.length === 0 && <div className={classes.noData}>No backup plans found for this device.</div>}
48
22
  {plans.length > 0 &&
49
- plans.map(({ id, title, sourceConfig, storage, isActive, method, size }) => (
23
+ plans.map(({ id, title, sourceConfig, storage, isActive, method, size, replicatedStorages }) => (
50
24
  <div key={id} className={classes.planItem}>
51
25
  <div className={`${classes.status} ${!isActive ? classes.paused : ''}`}>
52
26
  <div className={classes.iconBlock}>
@@ -63,7 +37,7 @@ const DeviceBackups = ({ plans }: DeviceBackupsProps) => {
63
37
  {sourceConfig.includes.length > 1 ? 'paths' : 'path'}
64
38
  </div>
65
39
  <div>
66
- <Icon type="storages" size={12} /> {storage.name}
40
+ <Icon type="storages" size={12} /> {replicatedStorages > 0 ? `${replicatedStorages + 1} storages` : storage.name}
67
41
  </div>
68
42
  <div>
69
43
  <Icon type="disk" size={12} /> {formatBytes(size)}
@@ -109,12 +83,13 @@ const DeviceBackups = ({ plans }: DeviceBackupsProps) => {
109
83
  <Icon type="storages" size={12} /> Connected Storages
110
84
  </div>
111
85
  <div className={classes.widgetContent}>
112
- {plans.length === 0 && <div className={classes.noData}>No Remote storages are being used by this device.</div>}
113
- {plans.length > 0 &&
114
- plansStorages.map(({ id, name, size, type, typeName }) => (
86
+ {storages && storages.length === 0 && <div className={classes.noData}>No Remote storages are being used by this device.</div>}
87
+ {storages &&
88
+ storages.length > 0 &&
89
+ storages.map(({ id, name, type, storageTypeName }) => (
115
90
  <div key={id} className={classes.planItem}>
116
91
  <div className={`${classes.status}`}>
117
- <div className={classes.iconBlock} title={typeName}>
92
+ <div className={classes.iconBlock}>
118
93
  {type === 'local' ? <Icon type="storages" size={12} /> : <img src={`/providers/${type}.png`} />}
119
94
  </div>
120
95
  </div>
@@ -122,7 +97,7 @@ const DeviceBackups = ({ plans }: DeviceBackupsProps) => {
122
97
  <h4>
123
98
  <NavLink to={`/storage/${id}`}>{name}</NavLink>
124
99
  </h4>
125
- <div className={classes.planStats}>{formatBytes(size)}</div>
100
+ <div className={classes.planStats}>{storageTypeName}</div>
126
101
  </div>
127
102
  </div>
128
103
  ))}
@@ -195,3 +195,68 @@
195
195
  }
196
196
  }
197
197
  }
198
+
199
+ .mirrorsEventsSection {
200
+ margin-top: 20px;
201
+ }
202
+
203
+ .mirrorEventsGroup {
204
+ margin-bottom: 16px;
205
+ }
206
+
207
+ .mirrorEventsHeader {
208
+ display: flex;
209
+ align-items: center;
210
+ gap: 8px;
211
+ padding: 8px 0;
212
+ font-weight: 600;
213
+ font-size: 0.95em;
214
+ }
215
+
216
+ .mirrorProviderIcon {
217
+ width: 20px;
218
+ height: 20px;
219
+ object-fit: contain;
220
+ }
221
+
222
+ .mirrorEventsName {
223
+ color: var(--content-text-color);
224
+ }
225
+
226
+ .mirrorEventsStatus {
227
+ padding: 2px 10px;
228
+ font-size: 0.85em;
229
+ border-radius: 12px;
230
+ text-transform: capitalize;
231
+ font-weight: 500;
232
+
233
+ &.mirror_pending {
234
+ background-color: var(--primary-color-lighter);
235
+ color: var(--content-text-color-light);
236
+ }
237
+ &.mirror_running {
238
+ background-color: var(--primary-color-lighter);
239
+ color: var(--primary-color);
240
+ }
241
+ &.mirror_completed {
242
+ background-color: var(--success-bg-color);
243
+ color: var(--success-text-color);
244
+ }
245
+ &.mirror_failed {
246
+ background-color: var(--error-bg-color);
247
+ color: var(--error-button-color);
248
+ }
249
+ }
250
+
251
+ .noEvents {
252
+ padding: 12px;
253
+ color: var(--content-text-color-light);
254
+ font-style: italic;
255
+ font-size: 0.9em;
256
+ }
257
+
258
+ .errorDetails {
259
+ padding: 10px;
260
+ word-break: break-word;
261
+ color: var(--error-button-color);
262
+ }
@@ -2,7 +2,7 @@ import Icon from '../../common/Icon/Icon';
2
2
  import SidePanel from '../../common/SidePanel/SidePanel';
3
3
  import { getBackupEventActionMessage, getRestoreEventActionMessage } from '../../../utils/progressHelpers';
4
4
  import classes from './BackupEvents.module.scss';
5
- import { BackupProgressData } from '../../../@types/backups';
5
+ import { BackupProgressData, ReplicationProgressData } from '../../../@types/backups';
6
6
  import { useGetBackupProgressOnce } from '../../../services/backups';
7
7
  import { useGetRestoreProgressOnce } from '../../../services/restores';
8
8
  import { useMemo, useState } from 'react';
@@ -21,7 +21,7 @@ interface BackupEventsProps {
21
21
  }
22
22
 
23
23
  const BackupEvents = ({ id, type = 'backup', sourceId, sourceType, planId, inProgress, progressData, close }: BackupEventsProps) => {
24
- const [showError, setShowError] = useState<false | number>(false);
24
+ const [showError, setShowError] = useState<false | string>(false);
25
25
  const { data: fetchedProgressData, isLoading } =
26
26
  type === 'backup'
27
27
  ? useGetBackupProgressOnce({ id, sourceId, sourceType, planId })
@@ -95,7 +95,7 @@ const BackupEvents = ({ id, type = 'backup', sourceId, sourceType, planId, inPro
95
95
  <span className={classes.action}>
96
96
  {type === 'backup' ? getBackupEventActionMessage(event.action) : getRestoreEventActionMessage(event.action)}
97
97
  {event.error && (
98
- <span className={classes.viewError} onClick={() => setShowError(index)}>
98
+ <span className={classes.viewError} onClick={() => setShowError(`main-${index}`)}>
99
99
  View Error
100
100
  </span>
101
101
  )}
@@ -105,10 +105,71 @@ const BackupEvents = ({ id, type = 'backup', sourceId, sourceType, planId, inPro
105
105
  })}
106
106
  </ul>
107
107
  ) : null}
108
+ {/* Mirror Events */}
109
+ {progressDataToUse?.mirrors && Object.keys(progressDataToUse.mirrors).length > 0 && (
110
+ <div className={classes.mirrorsEventsSection}>
111
+ {Object.entries(progressDataToUse.mirrors as Record<string, ReplicationProgressData>).map(([storageId, mirror]) => (
112
+ <div key={storageId} className={classes.mirrorEventsGroup}>
113
+ <div className={classes.mirrorEventsHeader}>
114
+ <img src={`/providers/${mirror.storageType}.png`} className={classes.mirrorProviderIcon} />
115
+ <span className={classes.mirrorEventsName}>{mirror.storageName}</span>
116
+ <span className={`${classes.mirrorEventsStatus} ${classes[`mirror_${mirror.status}`] || ''}`}>{mirror.status}</span>
117
+ </div>
118
+ {mirror.events && mirror.events.length > 0 ? (
119
+ <ul className={classes.eventList}>
120
+ {mirror.events.map((event: any, index: number) => {
121
+ const isError = event.error;
122
+ const isCompleted = event.action === 'REPLICATION_COMPLETE';
123
+ const isFailed = event.action === 'REPLICATION_FAILED';
124
+ return (
125
+ <li
126
+ key={index}
127
+ className={`${classes.eventItem} ${isError ? classes.error : ''} ${isCompleted ? classes.completed : ''} ${isFailed ? classes.failed : ''}`}
128
+ >
129
+ <span className={classes.icon}>
130
+ <Icon type={isError ? 'error-circle-filled' : 'check-circle-filled'} size={16} />
131
+ </span>
132
+ <span className={classes.time}>{new Date(event.timestamp).toLocaleTimeString()}</span>
133
+ <span className={classes.phase}>{event.phase}</span>
134
+ <span className={classes.action}>
135
+ {getBackupEventActionMessage(event.action, mirror.storageName)}
136
+ {event.error && (
137
+ <span className={classes.viewError} onClick={() => setShowError(`mirror-${storageId}-${index}`)}>
138
+ View Error
139
+ </span>
140
+ )}
141
+ </span>
142
+ </li>
143
+ );
144
+ })}
145
+ </ul>
146
+ ) : (
147
+ <div className={classes.noEvents}>No events recorded</div>
148
+ )}
149
+ </div>
150
+ ))}
151
+ </div>
152
+ )}
108
153
  </div>
109
154
  {showError && (
110
155
  <Modal title="Error Details" closeModal={() => setShowError(false)} width="400px">
111
- <div className={classes.errorDetails}>{progressDataToUse?.events[showError]?.error || 'Unknown error occurred.'}</div>
156
+ <div className={classes.errorDetails}>
157
+ {(() => {
158
+ if (typeof showError === 'string') {
159
+ if (showError.startsWith('main-')) {
160
+ const idx = parseInt(showError.replace('main-', ''));
161
+ return progressDataToUse?.events[idx]?.error || 'Unknown error occurred.';
162
+ }
163
+ if (showError.startsWith('mirror-')) {
164
+ const parts = showError.replace('mirror-', '').split('-');
165
+ const eventIdx = parseInt(parts.pop()!);
166
+ const sId = parts.join('-');
167
+ return (progressDataToUse?.mirrors as any)?.[sId]?.events?.[eventIdx]?.error || 'Unknown error occurred.';
168
+ }
169
+ }
170
+ return 'Unknown error occurred.';
171
+ })()}
172
+ </div>
112
173
  </Modal>
113
174
  )}
114
175
  </SidePanel>
@@ -10,13 +10,15 @@
10
10
  .backup {
11
11
  width: 100%;
12
12
  padding: 10px 15px;
13
- display: flex;
14
- justify-content: left;
15
- align-items: center;
16
13
  box-sizing: border-box;
17
14
  border-radius: 6px;
18
15
  border: 1px solid var(--line-color);
19
16
  margin-bottom: 15px;
17
+ .backupProgress {
18
+ display: flex;
19
+ justify-content: left;
20
+ align-items: center;
21
+ }
20
22
  .backupIcon {
21
23
  margin-right: 15px;
22
24
  }
@@ -118,3 +120,119 @@
118
120
  }
119
121
  }
120
122
  }
123
+
124
+ .mirrorsSection {
125
+ width: 100%;
126
+ margin-top: 10px;
127
+ padding: 10px;
128
+ box-sizing: border-box;
129
+ border-top: 1px solid var(--line-color);
130
+ }
131
+
132
+ .mirrorsTitle {
133
+ font-size: 0.6875rem;
134
+ font-weight: 600;
135
+ color: var(--content-text-color-light);
136
+ margin-bottom: 8px;
137
+ }
138
+
139
+ .mirrorsList {
140
+ display: flex;
141
+ flex-direction: column;
142
+ gap: 6px;
143
+ }
144
+
145
+ .mirrorItem {
146
+ display: flex;
147
+ flex-direction: row;
148
+ gap: 10px;
149
+ }
150
+ .mirrorProgress {
151
+ width: 100%;
152
+ display: flex;
153
+ flex-direction: column;
154
+ gap: 5px;
155
+ }
156
+
157
+ .mirrorStorageIcon {
158
+ padding: 5px;
159
+ border-radius: 4px;
160
+ border: 1px solid var(--line-color);
161
+ position: relative;
162
+ span {
163
+ position: absolute;
164
+ z-index: 2;
165
+ top: -5px;
166
+ margin-left: -10px;
167
+ }
168
+ img {
169
+ max-width: 20px;
170
+ vertical-align: middle;
171
+ }
172
+ }
173
+
174
+ .mirrorInfo {
175
+ display: flex;
176
+ justify-content: space-between;
177
+ align-items: center;
178
+ }
179
+
180
+ .mirrorName {
181
+ font-size: 0.7rem;
182
+ color: var(--content-text-color);
183
+ }
184
+
185
+ .mirrorStatus {
186
+ font-size: 0.7rem;
187
+ font-weight: 600;
188
+ color: var(--content-text-color-light);
189
+ &.mirrorCompleted {
190
+ color: var(--success-text-color);
191
+ }
192
+ &.mirrorFailed {
193
+ color: var(--error-button-color);
194
+ }
195
+ }
196
+
197
+ .mirrorBar {
198
+ width: 100%;
199
+ height: 6px;
200
+ border-radius: 3px;
201
+ background-color: var(--line-color);
202
+ overflow: hidden;
203
+ .mirrorBarFill {
204
+ height: 100%;
205
+ border-radius: 3px;
206
+ transition: all 0.3s linear;
207
+ &.mirrorCompleted {
208
+ background-color: turquoise;
209
+ }
210
+ &.mirrorFailed {
211
+ background-color: var(--error-button-color);
212
+ }
213
+ &.mirrorRunning {
214
+ background-color: turquoise;
215
+ }
216
+ &.mirrorPending {
217
+ background-color: var(--line-color);
218
+ width: 0;
219
+ }
220
+
221
+ &.mirrorRunningIndeterminate {
222
+ background-color: turquoise;
223
+ width: 100% !important;
224
+ opacity: 0.4;
225
+ animation: mirrorPulse 1.8s ease-in-out infinite;
226
+ }
227
+ }
228
+ }
229
+
230
+ @keyframes mirrorPulse {
231
+ 0%,
232
+ 100% {
233
+ opacity: 0.25;
234
+ }
235
+ 50% {
236
+ opacity: 0.6;
237
+ }
238
+ }
@@ -5,12 +5,18 @@ import Icon from '../../common/Icon/Icon';
5
5
  import { useCancelBackup, useGetBackupProgress } from '../../../services/backups';
6
6
  import classes from './BackupProgress.module.scss';
7
7
  import { formatBytes, formatDateTime, formatSeconds, timeAgo } from '../../../utils/helpers';
8
- import { generateBackupProgressMessage, extractResticData, generateRestoreProgressMessage } from '../../../utils/progressHelpers';
8
+ import {
9
+ generateBackupProgressMessage,
10
+ extractResticData,
11
+ generateRestoreProgressMessage,
12
+ generateMirrorProgressMessage,
13
+ } from '../../../utils/progressHelpers';
9
14
  import { Backup } from '../../../@types/backups';
10
15
  import { RestoreSlim } from '../../../@types/restores';
11
16
  import ActionModal from '../../common/ActionModal/ActionModal';
12
17
  import { useCancelRestore, useGetRestoreProgress } from '../../../services/restores';
13
18
  import BackupEvents from '../BackupEvents/BackupEvents';
19
+ import { ReplicationProgressData } from '../../../@types/backups';
14
20
 
15
21
  interface BackupProgressProps {
16
22
  item: Backup | RestoreSlim;
@@ -127,84 +133,156 @@ const BackupProgress = ({ item, sourceId, sourceType, planId, type = 'backup' }:
127
133
 
128
134
  return (
129
135
  <div key={item.id} className={classes.backup}>
130
- <div className={classes.backupIcon}>
131
- <Icon type="loading" size={24} />
132
- </div>
133
- <div className={classes.backupLeft}>
134
- <div className={classes.backupId}>
135
- {type === 'restore' ? 'Restoring ' : ''}backup-{id}
136
- <span className={classes.backupTime} title={formatDateTime(started)}>
137
- <Icon type="clock" size={12} /> Started {started ? timeAgo(new Date(started)) : 'a few seconds ago'}
138
- </span>
139
- {item.errorMsg && (
140
- <span className={classes.backupError}>
141
- <Icon type="error-circle-filled" size={13} />{' '}
142
- <i data-tooltip-id="htmlToolTip" data-tooltip-place="top" data-tooltip-html={item.errorMsg}>
143
- <u>Error</u> Occurred.
144
- </i>{' '}
145
- Retrying...
146
- </span>
147
- )}
136
+ <div key={item.id} className={classes.backupProgress}>
137
+ <div className={classes.backupIcon}>
138
+ <Icon type="loading" size={24} />
148
139
  </div>
149
- <div className={classes.backupStart}>
150
- <div>
151
- <span className={classes.progressMessage} onClick={() => setShowProgressDetails(true)}>
152
- {progressMessage}
140
+ <div className={classes.backupLeft}>
141
+ <div className={classes.backupId}>
142
+ {type === 'restore' ? 'Restoring ' : ''}backup-{id}
143
+ <span className={classes.backupTime} title={formatDateTime(started)}>
144
+ <Icon type="clock" size={12} /> Started {started ? timeAgo(new Date(started)) : 'a few seconds ago'}
153
145
  </span>
154
- {/* <Icon type="clock" size={12} /> Started {started ? timeAgo(new Date(started)) : 'a few seconds ago'} */}
146
+ {item.errorMsg && (
147
+ <span className={classes.backupError}>
148
+ <Icon type="error-circle-filled" size={13} />{' '}
149
+ <i data-tooltip-id="htmlToolTip" data-tooltip-place="top" data-tooltip-html={item.errorMsg}>
150
+ <u>Error</u> Occurred.
151
+ </i>{' '}
152
+ Retrying...
153
+ </span>
154
+ )}
155
155
  </div>
156
- <button onClick={() => setShowCancelModal(true)} title={`Cancel ${type}`}>
157
- <Icon type="close" size={12} /> Cancel
158
- </button>
159
- </div>
160
- </div>
161
- <div className={classes.backupRight}>
162
- <div className={classes.ProgressStats}>
163
- <div className={classes.ProgressStatsLeft}>
164
- <span>
165
- {filesProcessed} / {totalFiles} Files
166
- </span>
167
- <i></i>
168
- <span>
169
- {formatBytes(bytesProcessed)} / {formatBytes(totalBytes)}
170
- </span>
156
+ <div className={classes.backupStart}>
157
+ <div>
158
+ <span className={classes.progressMessage} onClick={() => setShowProgressDetails(true)}>
159
+ {progressMessage}
160
+ </span>
161
+ {/* <Icon type="clock" size={12} /> Started {started ? timeAgo(new Date(started)) : 'a few seconds ago'} */}
162
+ </div>
163
+ <button onClick={() => setShowCancelModal(true)} title={`Cancel ${type}`}>
164
+ <Icon type="close" size={12} /> Cancel
165
+ </button>
171
166
  </div>
172
- <div className={classes.ProgressStatsRight}>{type !== 'restore' && <>Remaining: {formatSeconds(seconds_remaining)}</>}</div>
173
167
  </div>
174
- <div className={classes.progressBar}>
175
- <div
176
- className={`${classes.progressBarFill} ${progressPercent > 3 ? classes.progressBarFilled : ''}`}
177
- style={{ width: progressPercent + '%' }}
178
- >
179
- <span>{progressPercent}%</span>
168
+ <div className={classes.backupRight}>
169
+ <div className={classes.ProgressStats}>
170
+ <div className={classes.ProgressStatsLeft}>
171
+ <span>
172
+ {filesProcessed} / {totalFiles} Files
173
+ </span>
174
+ <i></i>
175
+ <span>
176
+ {formatBytes(bytesProcessed)} / {formatBytes(totalBytes)}
177
+ </span>
178
+ </div>
179
+ <div className={classes.ProgressStatsRight}>{type !== 'restore' && <>Remaining: {formatSeconds(seconds_remaining)}</>}</div>
180
+ </div>
181
+ <div className={classes.progressBar}>
182
+ <div
183
+ className={`${classes.progressBarFill} ${progressPercent > 3 ? classes.progressBarFilled : ''}`}
184
+ style={{ width: progressPercent + '%' }}
185
+ >
186
+ <span>{progressPercent}%</span>
187
+ </div>
180
188
  </div>
181
189
  </div>
190
+
191
+ {showCancelModal && (
192
+ <ActionModal
193
+ title={`Cancel ${type}`}
194
+ message={<>{`Are you sure you want to cancel the ${type} process?`}</>}
195
+ closeModal={() => setShowCancelModal(false)}
196
+ width="400px"
197
+ primaryAction={{
198
+ title: `Yes, Cancel ${type}`,
199
+ type: 'danger',
200
+ isPending: type === 'restore' ? cancelRestoreMutation.isPending : cancelBackupMutation.isPending,
201
+ action: () => cancel(),
202
+ }}
203
+ />
204
+ )}
205
+ {showProgressDetails && (
206
+ <BackupEvents
207
+ id={id}
208
+ type={type}
209
+ planId={planId}
210
+ sourceId={sourceId}
211
+ sourceType={sourceType}
212
+ progressData={progressData}
213
+ inProgress={true}
214
+ close={() => setShowProgressDetails(false)}
215
+ />
216
+ )}
182
217
  </div>
183
- {showCancelModal && (
184
- <ActionModal
185
- title={`Cancel ${type}`}
186
- message={<>{`Are you sure you want to cancel the ${type} process?`}</>}
187
- closeModal={() => setShowCancelModal(false)}
188
- width="400px"
189
- primaryAction={{
190
- title: `Yes, Cancel ${type}`,
191
- type: 'danger',
192
- isPending: type === 'restore' ? cancelRestoreMutation.isPending : cancelBackupMutation.isPending,
193
- action: () => cancel(),
194
- }}
195
- />
196
- )}
197
- {showProgressDetails && (
198
- <BackupEvents
199
- id={id}
200
- type={type}
201
- planId={planId}
202
- sourceId={sourceId}
203
- sourceType={sourceType}
204
- progressData={progressData}
205
- inProgress={true}
206
- close={() => setShowProgressDetails(false)}
207
- />
218
+ {/* Mirrors Progress */}
219
+ {progressData?.mirrors && Object.keys(progressData.mirrors).length > 0 && (
220
+ <div className={classes.mirrorsSection}>
221
+ <div className={classes.mirrorsTitle}>Replicating</div>
222
+ <div className={classes.mirrorsList}>
223
+ {Object.entries(progressData.mirrors as Record<string, ReplicationProgressData>).map(([storageId, mirror]) => {
224
+ const mirrorResticData = mirror.events
225
+ ?.slice()
226
+ .reverse()
227
+ .find((e: any) => e.resticData)?.resticData;
228
+ const mirrorPercent = mirrorResticData?.percent_done ? Math.round(mirrorResticData.percent_done * 100) : 0;
229
+ const mirrorMessage = generateMirrorProgressMessage(mirror);
230
+
231
+ let mirrorStatusClass = classes.mirrorPending;
232
+ if (mirror.status === 'running')
233
+ mirrorStatusClass = mirrorPercent > 0 ? classes.mirrorRunning : classes.mirrorRunningIndeterminate;
234
+ if (mirror.status === 'completed') mirrorStatusClass = classes.mirrorCompleted;
235
+ if (mirror.status === 'failed') mirrorStatusClass = classes.mirrorFailed;
236
+ if (mirror.status === 'retrying') mirrorStatusClass = classes.mirrorRunningIndeterminate;
237
+
238
+ return (
239
+ <div key={storageId} className={classes.mirrorItem}>
240
+ <div className={classes.mirrorStorageIcon}>
241
+ {(mirror.status === 'running' || mirror.status === 'pending' || mirror.status === 'retrying') && (
242
+ <Icon type="loading" size={12} />
243
+ )}
244
+ <img src={`/providers/${mirror.storageType}.png`} />
245
+ </div>
246
+ <div className={classes.mirrorProgress}>
247
+ <div className={classes.mirrorInfo}>
248
+ <span className={classes.mirrorName}>{mirror.storageName}</span>
249
+ <span className={`${classes.mirrorStatus} ${mirrorStatusClass}`}>
250
+ {mirror.status === 'pending' && 'Pending'}
251
+ {mirror.status === 'running' && (mirrorPercent > 0 ? `${mirrorPercent}% — ${mirrorMessage}` : mirrorMessage)}
252
+ {mirror.status === 'completed' && (
253
+ <>
254
+ <Icon type="check" size={10} /> Completed
255
+ </>
256
+ )}
257
+ {mirror.status === 'failed' && (
258
+ <span data-tooltip-id="htmlToolTip" data-tooltip-html={mirror.error || 'Replication failed'}>
259
+ <Icon type="error" size={12} /> Failed
260
+ </span>
261
+ )}
262
+ {mirror.status === 'retrying' && mirrorMessage}
263
+ </span>
264
+ </div>
265
+ <div className={classes.mirrorBar}>
266
+ <div
267
+ className={`${classes.mirrorBarFill} ${mirrorStatusClass}`}
268
+ style={{
269
+ width:
270
+ mirror.status === 'completed'
271
+ ? '100%'
272
+ : mirror.status === 'failed'
273
+ ? '100%'
274
+ : mirrorPercent > 0
275
+ ? `${mirrorPercent}%`
276
+ : undefined,
277
+ }}
278
+ />
279
+ </div>
280
+ </div>
281
+ </div>
282
+ );
283
+ })}
284
+ </div>
285
+ </div>
208
286
  )}
209
287
  </div>
210
288
  );