@plutonhq/core-frontend 0.1.31 → 0.1.33

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 (143) hide show
  1. package/dist-lib/@types/plans.d.ts +4 -0
  2. package/dist-lib/@types/plans.d.ts.map +1 -1
  3. package/dist-lib/components/Plan/AddPlan/AddPlan.d.ts.map +1 -1
  4. package/dist-lib/components/Plan/AddPlan/AddPlan.js +29 -24
  5. package/dist-lib/components/Plan/AddPlan/AddPlan.js.map +1 -1
  6. package/dist-lib/components/Plan/BackupEvents/BackupEvents.js +33 -33
  7. package/dist-lib/components/Plan/BackupEvents/BackupEvents.js.map +1 -1
  8. package/dist-lib/components/Plan/BackupEvents/BackupEvents.module.scss.js +36 -36
  9. package/dist-lib/components/Plan/BackupProgress/BackupProgress.d.ts.map +1 -1
  10. package/dist-lib/components/Plan/BackupProgress/BackupProgress.js +63 -53
  11. package/dist-lib/components/Plan/BackupProgress/BackupProgress.js.map +1 -1
  12. package/dist-lib/components/Plan/BackupProgress/BackupProgress.module.scss.js +32 -32
  13. package/dist-lib/components/Plan/Backups/Backups.d.ts.map +1 -1
  14. package/dist-lib/components/Plan/Backups/Backups.js +148 -144
  15. package/dist-lib/components/Plan/Backups/Backups.js.map +1 -1
  16. package/dist-lib/components/Plan/Backups/Backups.module.scss.js +34 -32
  17. package/dist-lib/components/Plan/Backups/Backups.module.scss.js.map +1 -1
  18. package/dist-lib/components/Plan/FilterPlans/FilterPlans.d.ts +9 -0
  19. package/dist-lib/components/Plan/FilterPlans/FilterPlans.d.ts.map +1 -0
  20. package/dist-lib/components/Plan/FilterPlans/FilterPlans.js +117 -0
  21. package/dist-lib/components/Plan/FilterPlans/FilterPlans.js.map +1 -0
  22. package/dist-lib/components/Plan/FilterPlans/FilterPlans.module.scss.js +20 -0
  23. package/dist-lib/components/Plan/FilterPlans/FilterPlans.module.scss.js.map +1 -0
  24. package/dist-lib/components/Plan/PlanForm/PlanForm.d.ts +4 -2
  25. package/dist-lib/components/Plan/PlanForm/PlanForm.d.ts.map +1 -1
  26. package/dist-lib/components/Plan/PlanForm/PlanForm.js +33 -29
  27. package/dist-lib/components/Plan/PlanForm/PlanForm.js.map +1 -1
  28. package/dist-lib/components/Plan/PlanIntegrity/PlanIntegrity.d.ts +2 -1
  29. package/dist-lib/components/Plan/PlanIntegrity/PlanIntegrity.d.ts.map +1 -1
  30. package/dist-lib/components/Plan/PlanIntegrity/PlanIntegrity.js +85 -57
  31. package/dist-lib/components/Plan/PlanIntegrity/PlanIntegrity.js.map +1 -1
  32. package/dist-lib/components/Plan/PlanIntegrity/PlanIntegrity.module.scss.js +11 -9
  33. package/dist-lib/components/Plan/PlanIntegrity/PlanIntegrity.module.scss.js.map +1 -1
  34. package/dist-lib/components/Plan/PlanItems/PlanItem.js +1 -1
  35. package/dist-lib/components/Plan/PlanItems/PlanItem.js.map +1 -1
  36. package/dist-lib/components/Plan/PlanRepair/PlanRepair.d.ts +9 -0
  37. package/dist-lib/components/Plan/PlanRepair/PlanRepair.d.ts.map +1 -0
  38. package/dist-lib/components/Plan/PlanRepair/PlanRepair.js +262 -0
  39. package/dist-lib/components/Plan/PlanRepair/PlanRepair.js.map +1 -0
  40. package/dist-lib/components/Plan/PlanRepair/PlanRepair.module.scss.js +14 -0
  41. package/dist-lib/components/Plan/PlanRepair/PlanRepair.module.scss.js.map +1 -0
  42. package/dist-lib/components/Plan/PlanSettings/PlanAdvancedSettings.d.ts +4 -2
  43. package/dist-lib/components/Plan/PlanSettings/PlanAdvancedSettings.d.ts.map +1 -1
  44. package/dist-lib/components/Plan/PlanSettings/PlanAdvancedSettings.js +24 -22
  45. package/dist-lib/components/Plan/PlanSettings/PlanAdvancedSettings.js.map +1 -1
  46. package/dist-lib/components/Plan/PlanSettings/PlanGeneralSettings.d.ts +4 -2
  47. package/dist-lib/components/Plan/PlanSettings/PlanGeneralSettings.d.ts.map +1 -1
  48. package/dist-lib/components/Plan/PlanSettings/PlanGeneralSettings.js +39 -28
  49. package/dist-lib/components/Plan/PlanSettings/PlanGeneralSettings.js.map +1 -1
  50. package/dist-lib/components/Plan/PlanSettings/PlanSettings.module.scss.js +66 -64
  51. package/dist-lib/components/Plan/PlanSettings/PlanSettings.module.scss.js.map +1 -1
  52. package/dist-lib/components/Plan/PlanSizeChart/PlanSizeChart.d.ts +7 -0
  53. package/dist-lib/components/Plan/PlanSizeChart/PlanSizeChart.d.ts.map +1 -0
  54. package/dist-lib/components/Plan/PlanSizeChart/PlanSizeChart.js +116 -0
  55. package/dist-lib/components/Plan/PlanSizeChart/PlanSizeChart.js.map +1 -0
  56. package/dist-lib/components/Plan/PlanSizeChart/PlanSizeChart.module.scss.js +20 -0
  57. package/dist-lib/components/Plan/PlanSizeChart/PlanSizeChart.module.scss.js.map +1 -0
  58. package/dist-lib/components/Plan/PlanStats/PlanStats.d.ts.map +1 -1
  59. package/dist-lib/components/Plan/PlanStats/PlanStats.js +29 -30
  60. package/dist-lib/components/Plan/PlanStats/PlanStats.js.map +1 -1
  61. package/dist-lib/components/Plan/PlanStats/PlanStats.module.scss.js +16 -14
  62. package/dist-lib/components/Plan/PlanStats/PlanStats.module.scss.js.map +1 -1
  63. package/dist-lib/components/common/Icon/Icon.d.ts.map +1 -1
  64. package/dist-lib/components/common/Icon/Icon.js +395 -378
  65. package/dist-lib/components/common/Icon/Icon.js.map +1 -1
  66. package/dist-lib/components/common/SortItems/SortItems.d.ts +2 -1
  67. package/dist-lib/components/common/SortItems/SortItems.d.ts.map +1 -1
  68. package/dist-lib/components/common/SortItems/SortItems.js +14 -14
  69. package/dist-lib/components/common/SortItems/SortItems.js.map +1 -1
  70. package/dist-lib/components/common/SortItems/SortItems.module.scss.js +1 -1
  71. package/dist-lib/components/common/form/MultiSelect/MultiSelect.module.scss.js +16 -16
  72. package/dist-lib/components/index.d.ts +2 -0
  73. package/dist-lib/components/index.d.ts.map +1 -1
  74. package/dist-lib/components.js +199 -195
  75. package/dist-lib/components.js.map +1 -1
  76. package/dist-lib/hooks/usePlanSingleActions.d.ts.map +1 -1
  77. package/dist-lib/hooks/usePlanSingleActions.js +22 -19
  78. package/dist-lib/hooks/usePlanSingleActions.js.map +1 -1
  79. package/dist-lib/node_modules/.pnpm/@kurkle_color@0.3.4/node_modules/@kurkle/color/dist/color.esm.js +449 -0
  80. package/dist-lib/node_modules/.pnpm/@kurkle_color@0.3.4/node_modules/@kurkle/color/dist/color.esm.js.map +1 -0
  81. package/dist-lib/node_modules/.pnpm/chart.js@4.5.1/node_modules/chart.js/dist/chart.js +5219 -0
  82. package/dist-lib/node_modules/.pnpm/chart.js@4.5.1/node_modules/chart.js/dist/chart.js.map +1 -0
  83. package/dist-lib/node_modules/.pnpm/chart.js@4.5.1/node_modules/chart.js/dist/chunks/helpers.dataset.js +1691 -0
  84. package/dist-lib/node_modules/.pnpm/chart.js@4.5.1/node_modules/chart.js/dist/chunks/helpers.dataset.js.map +1 -0
  85. package/dist-lib/node_modules/.pnpm/react-chartjs-2@5.3.1_chart.js@4.5.1_react@18.3.1/node_modules/react-chartjs-2/dist/index.js +93 -0
  86. package/dist-lib/node_modules/.pnpm/react-chartjs-2@5.3.1_chart.js@4.5.1_react@18.3.1/node_modules/react-chartjs-2/dist/index.js.map +1 -0
  87. package/dist-lib/routes/Login/Login.d.ts.map +1 -1
  88. package/dist-lib/routes/Login/Login.js +45 -36
  89. package/dist-lib/routes/Login/Login.js.map +1 -1
  90. package/dist-lib/routes/PlanSingle/PlanSingle.d.ts.map +1 -1
  91. package/dist-lib/routes/PlanSingle/PlanSingle.js +131 -118
  92. package/dist-lib/routes/PlanSingle/PlanSingle.js.map +1 -1
  93. package/dist-lib/routes/Plans/Plans.d.ts.map +1 -1
  94. package/dist-lib/routes/Plans/Plans.js +77 -51
  95. package/dist-lib/routes/Plans/Plans.js.map +1 -1
  96. package/dist-lib/services/plans.d.ts +33 -5
  97. package/dist-lib/services/plans.d.ts.map +1 -1
  98. package/dist-lib/services/plans.js +92 -67
  99. package/dist-lib/services/plans.js.map +1 -1
  100. package/dist-lib/services.js +93 -91
  101. package/dist-lib/styles/core-frontend.css +1 -1
  102. package/dist-lib/styles/global.scss +4 -0
  103. package/dist-lib/utils/helpers.d.ts +2 -0
  104. package/dist-lib/utils/helpers.d.ts.map +1 -1
  105. package/dist-lib/utils/helpers.js +68 -42
  106. package/dist-lib/utils/helpers.js.map +1 -1
  107. package/dist-lib/utils.js +36 -34
  108. package/package.json +3 -1
  109. package/src/@types/plans.ts +5 -0
  110. package/src/components/Plan/AddPlan/AddPlan.tsx +22 -16
  111. package/src/components/Plan/BackupEvents/BackupEvents.module.scss +2 -0
  112. package/src/components/Plan/BackupEvents/BackupEvents.tsx +2 -2
  113. package/src/components/Plan/BackupProgress/BackupProgress.module.scss +1 -0
  114. package/src/components/Plan/BackupProgress/BackupProgress.tsx +7 -2
  115. package/src/components/Plan/Backups/Backups.module.scss +16 -0
  116. package/src/components/Plan/Backups/Backups.tsx +13 -2
  117. package/src/components/Plan/FilterPlans/FilterPlans.module.scss +65 -0
  118. package/src/components/Plan/FilterPlans/FilterPlans.tsx +126 -0
  119. package/src/components/Plan/PlanForm/PlanForm.tsx +7 -1
  120. package/src/components/Plan/PlanIntegrity/PlanIntegrity.module.scss +19 -0
  121. package/src/components/Plan/PlanIntegrity/PlanIntegrity.tsx +40 -3
  122. package/src/components/Plan/PlanItems/PlanItem.tsx +1 -1
  123. package/src/components/Plan/PlanRepair/PlanRepair.module.scss +53 -0
  124. package/src/components/Plan/PlanRepair/PlanRepair.tsx +243 -0
  125. package/src/components/Plan/PlanSettings/PlanAdvancedSettings.tsx +6 -2
  126. package/src/components/Plan/PlanSettings/PlanGeneralSettings.tsx +14 -2
  127. package/src/components/Plan/PlanSettings/PlanSettings.module.scss +8 -0
  128. package/src/components/Plan/PlanSizeChart/PlanSizeChart.module.scss +76 -0
  129. package/src/components/Plan/PlanSizeChart/PlanSizeChart.tsx +163 -0
  130. package/src/components/Plan/PlanStats/PlanStats.module.scss +16 -2
  131. package/src/components/Plan/PlanStats/PlanStats.tsx +8 -11
  132. package/src/components/common/Icon/Icon.tsx +21 -0
  133. package/src/components/common/SortItems/SortItems.module.scss +3 -2
  134. package/src/components/common/SortItems/SortItems.tsx +6 -3
  135. package/src/components/common/form/MultiSelect/MultiSelect.module.scss +1 -0
  136. package/src/components/index.ts +2 -0
  137. package/src/hooks/usePlanSingleActions.tsx +26 -23
  138. package/src/routes/Login/Login.tsx +8 -2
  139. package/src/routes/PlanSingle/PlanSingle.tsx +17 -0
  140. package/src/routes/Plans/Plans.tsx +70 -35
  141. package/src/services/plans.ts +40 -4
  142. package/src/styles/global.scss +4 -0
  143. package/src/utils/helpers.ts +25 -0
@@ -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
  };
@@ -0,0 +1,65 @@
1
+ .filterItems {
2
+ position: relative;
3
+ .filterBtn {
4
+ display: flex;
5
+ align-items: center;
6
+ .filterCount {
7
+ background-color: var(--primary-color);
8
+ color: var(--primary-text-color);
9
+ padding: 1px 6px;
10
+ border-radius: 12px;
11
+ font-size: 10px;
12
+ }
13
+ &.filterBtnActive {
14
+ color: var(--primary-color);
15
+ }
16
+ }
17
+ .dropdown {
18
+ margin-top: -1px;
19
+ position: absolute;
20
+ z-index: 9999;
21
+ border: 1px solid var(--line-color);
22
+ background: var(--field-bg);
23
+ box-shadow: 0 0 15px var(--content-shadow-color);
24
+ width: calc(100% - 2px);
25
+ border-radius: 0 0 4px 4px;
26
+ overflow: auto;
27
+ font-size: 0.75rem;
28
+ right: 0;
29
+ width: max-content;
30
+ max-height: 320px;
31
+ border-radius: 6px;
32
+
33
+ .field {
34
+ padding: 12px 12px 0 12px;
35
+ label {
36
+ font-weight: 600;
37
+ margin-bottom: 4px;
38
+ display: block;
39
+ }
40
+ }
41
+ .footer {
42
+ border-top: 1px solid var(--line-color);
43
+ margin-top: 12px;
44
+ display: flex;
45
+ justify-content: space-around;
46
+ button:nth-child(2) {
47
+ padding: 6px 10px;
48
+ background: var(--primary-color-light);
49
+ color: var(--primary-color);
50
+ line-height: 1em;
51
+ height: fit-content;
52
+ border-radius: 4px;
53
+ margin: 8px;
54
+ font-weight: 600;
55
+ &:hover {
56
+ background: var(--primary-color);
57
+ color: var(--primary-text-color);
58
+ }
59
+ }
60
+ button:hover {
61
+ color: var(--primary-color);
62
+ }
63
+ }
64
+ }
65
+ }
@@ -0,0 +1,126 @@
1
+ import { useMemo, useState } from 'react';
2
+ import classes from './FilterPlans.module.scss';
3
+ import { Plan } from '../../..';
4
+ import { Icon, MultiSelect } from '../..';
5
+
6
+ type FilterPlansProps = {
7
+ onUpdate: (s: Record<PlanFilterTypes, string[]> | null) => void;
8
+ plans: Plan[];
9
+ };
10
+ export type PlanFilterTypes = 'devices' | 'tags' | 'storages' | 'methods';
11
+
12
+ const defaultState = { devices: [], tags: [], storages: [], methods: [] };
13
+
14
+ const FilterPlans = ({ onUpdate, plans }: FilterPlansProps) => {
15
+ const [showDropDown, setShowDropDown] = useState(false);
16
+ const [selected, setSelected] = useState<Record<PlanFilterTypes, string[]>>(() => {
17
+ const storedSettingsRaw = localStorage.getItem('plans_filter');
18
+ const storedSettings = storedSettingsRaw ? JSON.parse(storedSettingsRaw) : null;
19
+ return storedSettings || defaultState;
20
+ });
21
+
22
+ const hasFilters = Object.values(selected).some((arr) => arr.length > 0);
23
+
24
+ const filterableItems = useMemo(() => {
25
+ const items: Record<PlanFilterTypes, { label: string; value: string }[]> = { devices: [], tags: [], storages: [], methods: [] };
26
+ plans.forEach((plan) => {
27
+ plan.tags.forEach((tag) => {
28
+ if (!items.tags.find((t) => t.value === tag)) {
29
+ items.tags.push({ label: tag, value: tag });
30
+ }
31
+ });
32
+ if (!items.devices.find((t) => t.value === plan.device.id)) {
33
+ items.devices.push({ label: plan.device.name, value: plan.device.id });
34
+ }
35
+ if (!items.storages.find((t) => t.value === plan.storage.id)) {
36
+ items.storages.push({ label: plan.storage.name, value: plan.storage.id });
37
+ }
38
+ if (!items.methods.find((t) => t.value === plan.method)) {
39
+ items.methods.push({ label: plan.method, value: plan.method });
40
+ }
41
+ });
42
+ return items;
43
+ }, [plans]);
44
+
45
+ return (
46
+ <div className={classes.filterItems}>
47
+ <button
48
+ className={`${classes.filterBtn} ${showDropDown || hasFilters ? classes.filterBtnActive : ''}`}
49
+ onClick={() => setShowDropDown(!showDropDown)}
50
+ data-tooltip-id="appTooltip"
51
+ data-tooltip-content="Filter"
52
+ aria-label="Filter Plans"
53
+ data-tooltip-place="top"
54
+ data-tooltip-delay-show={500}
55
+ >
56
+ <Icon type="filter" size={18} />{' '}
57
+ {hasFilters && <span className={classes.filterCount}>{Object.values(selected).reduce((acc, arr) => acc + arr.length, 0)}</span>}
58
+ </button>
59
+ {showDropDown && (
60
+ <div className={`${classes.dropdown} styled__scrollbar`}>
61
+ {filterableItems.devices.length > 1 && (
62
+ <div className={classes.field}>
63
+ <label>Devices</label>
64
+ <MultiSelect
65
+ title="Devices"
66
+ fieldValue={selected.devices}
67
+ options={filterableItems.devices}
68
+ onUpdate={(devices) => setSelected((selected) => ({ ...selected, devices }))}
69
+ />
70
+ </div>
71
+ )}
72
+ <div className={classes.field}>
73
+ <label>Backup Type</label>
74
+ <MultiSelect
75
+ title="Backup Type"
76
+ fieldValue={selected.methods}
77
+ options={filterableItems.methods}
78
+ onUpdate={(methods) => setSelected((selected) => ({ ...selected, methods }))}
79
+ />
80
+ </div>
81
+ <div className={classes.field}>
82
+ <label>Storage</label>
83
+ <MultiSelect
84
+ title="Storages"
85
+ fieldValue={selected.storages}
86
+ options={filterableItems.storages}
87
+ onUpdate={(storages) => setSelected((selected) => ({ ...selected, storages }))}
88
+ />
89
+ </div>
90
+ <div className={classes.field}>
91
+ <label>Tags</label>
92
+ <MultiSelect
93
+ title="Tags"
94
+ fieldValue={selected.tags}
95
+ options={filterableItems.tags}
96
+ onUpdate={(tags) => setSelected((selected) => ({ ...selected, tags }))}
97
+ />
98
+ </div>
99
+ <div className={classes.footer}>
100
+ <button
101
+ onClick={() => {
102
+ localStorage.removeItem('plans_filter');
103
+ setSelected(defaultState);
104
+ onUpdate(null);
105
+ setShowDropDown(false);
106
+ }}
107
+ >
108
+ <Icon type="reload" /> Reset
109
+ </button>
110
+ <button
111
+ onClick={() => {
112
+ localStorage.setItem('plans_filter', JSON.stringify(selected));
113
+ onUpdate(selected);
114
+ setShowDropDown(false);
115
+ }}
116
+ >
117
+ <Icon type="check" size={12} /> Apply
118
+ </button>
119
+ </div>
120
+ </div>
121
+ )}
122
+ </div>
123
+ );
124
+ };
125
+
126
+ export default FilterPlans;
@@ -4,7 +4,7 @@ import SidePanel from '../../common/SidePanel/SidePanel';
4
4
  import StoragePicker from '../../common/form/StoragePicker/StoragePicker';
5
5
  import PlanStrategySettings from '../PlanSettings/PlanStrategySettings';
6
6
  import PlanSourceSettings from '../PlanSettings/PlanSourceSettings';
7
- import { NewPlanSettings } from '../../../@types/plans';
7
+ import { NewPlanSettings, PlanAddRunSettings } from '../../../@types/plans';
8
8
  import classes from '../AddPlan/AddPlan.module.scss';
9
9
  import PFClasses from './PlanForm.module.scss';
10
10
  import { useGetSettings } from '../../../services/settings';
@@ -27,6 +27,8 @@ type PlanFormProps = {
27
27
  storagePath?: string;
28
28
  storageId?: string;
29
29
  planId?: string;
30
+ runSettings?: PlanAddRunSettings;
31
+ setRunSettings?: (runSettings: PlanAddRunSettings) => void;
30
32
  };
31
33
 
32
34
  const PlanForm = ({
@@ -40,6 +42,8 @@ const PlanForm = ({
40
42
  storagePath,
41
43
  storageId,
42
44
  planId,
45
+ runSettings,
46
+ setRunSettings,
43
47
  }: PlanFormProps) => {
44
48
  const [step, setStep] = useState<number>(1);
45
49
 
@@ -279,6 +283,8 @@ const PlanForm = ({
279
283
  onUpdate={onPlanSettingsChange}
280
284
  device={deviceInstance}
281
285
  isEditing={type === 'edit'}
286
+ runSettings={runSettings}
287
+ setRunSettings={setRunSettings}
282
288
  />
283
289
  </div>
284
290
  )}
@@ -103,6 +103,25 @@
103
103
  }
104
104
  }
105
105
 
106
+ .repairBox {
107
+ border: 1px solid var(--line-color);
108
+ padding: 12px;
109
+ margin: 15px 5px;
110
+ box-sizing: border-box;
111
+ border-radius: 6px;
112
+ border-color: var(--primary-color-mid) #aaacff;
113
+ background: var(--background-color);
114
+
115
+ button {
116
+ background-color: var(--primary-color);
117
+ color: var(--primary-text-color);
118
+ padding: 6px 12px;
119
+ cursor: pointer;
120
+ border-radius: 4px;
121
+ margin-top: 6px;
122
+ }
123
+ }
124
+
106
125
  @media only screen and (max-width: 768px) {
107
126
  .integrityLogs .integrityLogsHeader {
108
127
  flex-direction: column;
@@ -13,9 +13,10 @@ interface PlanIntegrityProps {
13
13
  storage: { name: string; type: string; id: string };
14
14
  replicationStorages: PlanReplicationStorage[];
15
15
  onClose: () => void;
16
+ onRepairOpen: (replicationId: string) => void;
16
17
  }
17
18
 
18
- const PlanIntegrity = ({ planId, taskPending, verificationData, storage, replicationStorages = [], onClose }: PlanIntegrityProps) => {
19
+ const PlanIntegrity = ({ planId, taskPending, verificationData, storage, replicationStorages = [], onClose, onRepairOpen }: PlanIntegrityProps) => {
19
20
  const [showContent, setShowContent] = useState('primary');
20
21
  const integrityCheckMutation = useCheckPlanIntegrity();
21
22
 
@@ -67,6 +68,11 @@ const PlanIntegrity = ({ planId, taskPending, verificationData, storage, replica
67
68
  .split('\n')
68
69
  .map((line, index) => <p key={index}>{line.includes('`') ? <code>{line.replace(/`/g, '')}</code> : line}</p>)}
69
70
  {!backupResData.fix && <p>No Suggestion for this issue.</p>}
71
+ {(backupResData.errorType === 'pack_file_error' || backupResData.errorType === 'index_error') && (
72
+ <p>
73
+ <button onClick={() => onRepairOpen(storageType)}>Open Repo Repair</button> Window for more options.
74
+ </p>
75
+ )}
70
76
  <p>
71
77
  Learn more about fixing restic repo issues{' '}
72
78
  <a
@@ -96,6 +102,37 @@ const PlanIntegrity = ({ planId, taskPending, verificationData, storage, replica
96
102
  return <div className="label error">⛔ Error Found. {backupMessage}</div>;
97
103
  };
98
104
 
105
+ const renderRepairContent = (storageType: string) => {
106
+ const backupResData = getBackupResultByStorage(storageType);
107
+ return (
108
+ <div className={classes.repairBox}>
109
+ {(backupResData?.errorType === 'pack_file_error' || backupResData?.errorType === 'repairable_pack_file_error') && (
110
+ <div>
111
+ Some pack files in the repository are either damaged or missing and can be repaired.{' '}
112
+ <button onClick={() => onRepairOpen(storageType)}>
113
+ <Icon type="repair" size={13} /> Open Repo Repair Tool
114
+ </button>
115
+ </div>
116
+ )}
117
+ {backupResData?.errorType === 'index_error' && (
118
+ <div>
119
+ The index files in the repository are corrupted and can be repaired.{' '}
120
+ <button onClick={() => onRepairOpen(storageType)}>
121
+ <Icon type="repair" size={13} /> Open Repo Repair Tool
122
+ </button>{' '}
123
+ </div>
124
+ )}
125
+ </div>
126
+ );
127
+ };
128
+
129
+ const renderRepairOrFixSuggestion = (storageType: string) => {
130
+ const backupResData = getBackupResultByStorage(storageType);
131
+ const errorType = backupResData?.errorType;
132
+ const isRepairable = errorType === 'pack_file_error' || errorType === 'index_error' || errorType === 'repairable_pack_file_error';
133
+ return isRepairable ? renderRepairContent(storageType) : renderBackupFixSuggestion(storageType);
134
+ };
135
+
99
136
  const renderResultWithReplications = () => {
100
137
  return (
101
138
  <div className={`${classes.integrityResult} ${classes.withReplications}`}>
@@ -130,7 +167,7 @@ const PlanIntegrity = ({ planId, taskPending, verificationData, storage, replica
130
167
  <div className={classes.replicationResultContent}>
131
168
  {renderBackupStatus(storageType)}
132
169
  {renderLogs(storageType)}
133
- {renderBackupFixSuggestion(storageType)}
170
+ {renderRepairOrFixSuggestion(storageType)}
134
171
  </div>
135
172
  )}
136
173
  </div>
@@ -152,7 +189,7 @@ const PlanIntegrity = ({ planId, taskPending, verificationData, storage, replica
152
189
  <div className={classes.integrityResult}>
153
190
  {renderBackupStatus('primary')}
154
191
  {renderLogs('primary')}
155
- {renderBackupFixSuggestion('primary')}
192
+ {renderRepairOrFixSuggestion('primary')}
156
193
  </div>
157
194
  ) : (
158
195
  renderResultWithReplications()
@@ -79,7 +79,7 @@ const PlanItem = ({ plan, layout = 'list' }: PlanItemProps) => {
79
79
  return;
80
80
  }
81
81
  toast.promise(
82
- performBackupMutation.mutateAsync(id),
82
+ performBackupMutation.mutateAsync({ id }),
83
83
  {
84
84
  pending: `Starting ${isSync ? 'Sync' : 'Backup'}...`,
85
85
  success: `${isSync ? 'Sync' : 'Backup'} initiated successfully! 🚀`,
@@ -0,0 +1,53 @@
1
+ .repairContainer {
2
+ position: relative;
3
+ small {
4
+ padding: 12px 0;
5
+ display: block;
6
+ }
7
+ .repairContent {
8
+ border: 1px solid var(--line-color);
9
+ border-radius: 6px;
10
+ button {
11
+ background: var(--background-color);
12
+ border-radius: 20px;
13
+ line-height: 1.7em;
14
+ color: var(--primary-color);
15
+ padding: 2px 10px;
16
+ transition: all 0.1s linear;
17
+ &:hover {
18
+ background: var(--primary-color);
19
+ color: var(--primary-text-color);
20
+ }
21
+ }
22
+ .repairTitle {
23
+ background: var(--background-color);
24
+ margin-top: 0;
25
+ font-weight: 600;
26
+ font-size: 1.1em;
27
+ padding: 12px;
28
+ }
29
+ p {
30
+ padding: 16px 8px;
31
+ border-bottom: 1px solid var(--line-color);
32
+ margin: 0;
33
+ }
34
+ p:last-child {
35
+ border-bottom: none;
36
+ }
37
+ }
38
+ .overlay {
39
+ position: absolute;
40
+ width: 100%;
41
+ height: 100%;
42
+ top: 0;
43
+ left: 0;
44
+ opacity: 0.95;
45
+ background-color: var(--modal-bg);
46
+ z-index: 3;
47
+ justify-content: center;
48
+ align-items: center;
49
+ display: flex;
50
+ border: 1px solid var(--line-color);
51
+ border-radius: 6px;
52
+ }
53
+ }