@plutonhq/core-frontend 0.1.24 → 0.1.26

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 (121) hide show
  1. package/dist-lib/@types/backups.d.ts +1 -1
  2. package/dist-lib/@types/backups.d.ts.map +1 -1
  3. package/dist-lib/components/Plan/BackupEvents/BackupEvents.d.ts.map +1 -1
  4. package/dist-lib/components/Plan/BackupEvents/BackupEvents.js +27 -27
  5. package/dist-lib/components/Plan/BackupEvents/BackupEvents.js.map +1 -1
  6. package/dist-lib/components/Plan/Backups/Backups.d.ts.map +1 -1
  7. package/dist-lib/components/Plan/Backups/Backups.js +189 -159
  8. package/dist-lib/components/Plan/Backups/Backups.js.map +1 -1
  9. package/dist-lib/components/Plan/PlanPruneModal/PlanPruneModal.d.ts.map +1 -1
  10. package/dist-lib/components/Plan/PlanPruneModal/PlanPruneModal.js +62 -26
  11. package/dist-lib/components/Plan/PlanPruneModal/PlanPruneModal.js.map +1 -1
  12. package/dist-lib/components/Plan/PlanSettings/PlanPruneSettings.d.ts.map +1 -1
  13. package/dist-lib/components/Plan/PlanSettings/PlanPruneSettings.js +138 -62
  14. package/dist-lib/components/Plan/PlanSettings/PlanPruneSettings.js.map +1 -1
  15. package/dist-lib/components/Plan/PlanSettings/PlanSettings.module.scss.js +42 -42
  16. package/dist-lib/components/Plan/SnapshotViewer/SnapshotViewer.d.ts +32 -0
  17. package/dist-lib/components/Plan/SnapshotViewer/SnapshotViewer.d.ts.map +1 -0
  18. package/dist-lib/components/Plan/SnapshotViewer/SnapshotViewer.js +252 -0
  19. package/dist-lib/components/Plan/SnapshotViewer/SnapshotViewer.js.map +1 -0
  20. package/dist-lib/components/Plan/SnapshotViewer/SnapshotViewerFile.d.ts +23 -0
  21. package/dist-lib/components/Plan/SnapshotViewer/SnapshotViewerFile.d.ts.map +1 -0
  22. package/dist-lib/components/Plan/SnapshotViewer/SnapshotViewerFile.js +72 -0
  23. package/dist-lib/components/Plan/SnapshotViewer/SnapshotViewerFile.js.map +1 -0
  24. package/dist-lib/components/Restore/RestoreFileSelector/RestoreFileSelector.d.ts.map +1 -1
  25. package/dist-lib/components/Restore/RestoreFileSelector/RestoreFileSelector.js +188 -198
  26. package/dist-lib/components/Restore/RestoreFileSelector/RestoreFileSelector.js.map +1 -1
  27. package/dist-lib/components/Restore/RestoreFileSelector/RestoreFileSelector.module.scss.js +20 -64
  28. package/dist-lib/components/Restore/RestoreFileSelector/RestoreFileSelector.module.scss.js.map +1 -1
  29. package/dist-lib/components/Restore/RestoredFileBrowser/RestoredFileBrowser.d.ts.map +1 -1
  30. package/dist-lib/components/Restore/RestoredFileBrowser/RestoredFileBrowser.js +125 -159
  31. package/dist-lib/components/Restore/RestoredFileBrowser/RestoredFileBrowser.js.map +1 -1
  32. package/dist-lib/components/common/SnapshotBrowser/SnapshotBrowser.module.scss.js +74 -0
  33. package/dist-lib/components/common/SnapshotBrowser/SnapshotBrowser.module.scss.js.map +1 -0
  34. package/dist-lib/components/common/SnapshotBrowser/SnapshotBrowserDirectories.d.ts +17 -0
  35. package/dist-lib/components/common/SnapshotBrowser/SnapshotBrowserDirectories.d.ts.map +1 -0
  36. package/dist-lib/components/common/SnapshotBrowser/SnapshotBrowserDirectories.js +57 -0
  37. package/dist-lib/components/common/SnapshotBrowser/SnapshotBrowserDirectories.js.map +1 -0
  38. package/dist-lib/components/common/SnapshotBrowser/SnapshotBrowserFileList.d.ts +18 -0
  39. package/dist-lib/components/common/SnapshotBrowser/SnapshotBrowserFileList.d.ts.map +1 -0
  40. package/dist-lib/components/common/SnapshotBrowser/SnapshotBrowserFileList.js +24 -0
  41. package/dist-lib/components/common/SnapshotBrowser/SnapshotBrowserFileList.js.map +1 -0
  42. package/dist-lib/components/common/SnapshotBrowser/SnapshotBrowserFileRow.d.ts +18 -0
  43. package/dist-lib/components/common/SnapshotBrowser/SnapshotBrowserFileRow.d.ts.map +1 -0
  44. package/dist-lib/components/common/SnapshotBrowser/SnapshotBrowserFileRow.js +37 -0
  45. package/dist-lib/components/common/SnapshotBrowser/SnapshotBrowserFileRow.js.map +1 -0
  46. package/dist-lib/components/common/SnapshotBrowser/SnapshotBrowserGoUpRow.d.ts +9 -0
  47. package/dist-lib/components/common/SnapshotBrowser/SnapshotBrowserGoUpRow.d.ts.map +1 -0
  48. package/dist-lib/components/common/SnapshotBrowser/SnapshotBrowserGoUpRow.js +15 -0
  49. package/dist-lib/components/common/SnapshotBrowser/SnapshotBrowserGoUpRow.js.map +1 -0
  50. package/dist-lib/components/common/SnapshotBrowser/SnapshotBrowserToolbar.d.ts +11 -0
  51. package/dist-lib/components/common/SnapshotBrowser/SnapshotBrowserToolbar.d.ts.map +1 -0
  52. package/dist-lib/components/common/SnapshotBrowser/SnapshotBrowserToolbar.js +18 -0
  53. package/dist-lib/components/common/SnapshotBrowser/SnapshotBrowserToolbar.js.map +1 -0
  54. package/dist-lib/components/common/SnapshotBrowser/hooks/useSnapshotDatabase.d.ts +12 -0
  55. package/dist-lib/components/common/SnapshotBrowser/hooks/useSnapshotDatabase.d.ts.map +1 -0
  56. package/dist-lib/components/common/SnapshotBrowser/hooks/useSnapshotDatabase.js +70 -0
  57. package/dist-lib/components/common/SnapshotBrowser/hooks/useSnapshotDatabase.js.map +1 -0
  58. package/dist-lib/components/common/SnapshotBrowser/hooks/useSnapshotNavigation.d.ts +22 -0
  59. package/dist-lib/components/common/SnapshotBrowser/hooks/useSnapshotNavigation.d.ts.map +1 -0
  60. package/dist-lib/components/common/SnapshotBrowser/hooks/useSnapshotNavigation.js +79 -0
  61. package/dist-lib/components/common/SnapshotBrowser/hooks/useSnapshotNavigation.js.map +1 -0
  62. package/dist-lib/components/common/SnapshotBrowser/hooks/useSnapshotSort.d.ts +6 -0
  63. package/dist-lib/components/common/SnapshotBrowser/hooks/useSnapshotSort.d.ts.map +1 -0
  64. package/dist-lib/components/common/SnapshotBrowser/hooks/useSnapshotSort.js +18 -0
  65. package/dist-lib/components/common/SnapshotBrowser/hooks/useSnapshotSort.js.map +1 -0
  66. package/dist-lib/components/common/SnapshotBrowser/index.d.ts +11 -0
  67. package/dist-lib/components/common/SnapshotBrowser/index.d.ts.map +1 -0
  68. package/dist-lib/components/common/StatusLabel/StatusLabel.d.ts +1 -1
  69. package/dist-lib/components/common/StatusLabel/StatusLabel.d.ts.map +1 -1
  70. package/dist-lib/components/common/StatusLabel/StatusLabel.js +17 -12
  71. package/dist-lib/components/common/StatusLabel/StatusLabel.js.map +1 -1
  72. package/dist-lib/components/common/form/NumberInput/NumberInput.module.scss.js +4 -4
  73. package/dist-lib/components/index.d.ts +10 -0
  74. package/dist-lib/components/index.d.ts.map +1 -1
  75. package/dist-lib/components.js +152 -132
  76. package/dist-lib/components.js.map +1 -1
  77. package/dist-lib/hooks/usePlanSingleActions.d.ts.map +1 -1
  78. package/dist-lib/hooks/usePlanSingleActions.js +21 -21
  79. package/dist-lib/hooks/usePlanSingleActions.js.map +1 -1
  80. package/dist-lib/services/backups.d.ts +4 -0
  81. package/dist-lib/services/backups.d.ts.map +1 -1
  82. package/dist-lib/services/backups.js +34 -25
  83. package/dist-lib/services/backups.js.map +1 -1
  84. package/dist-lib/services.js +113 -112
  85. package/dist-lib/styles/core-frontend.css +1 -1
  86. package/dist-lib/utils/index.d.ts +1 -0
  87. package/dist-lib/utils/index.d.ts.map +1 -1
  88. package/dist-lib/utils/snapshotDatabase.d.ts +16 -0
  89. package/dist-lib/utils/snapshotDatabase.d.ts.map +1 -0
  90. package/dist-lib/utils/snapshotDatabase.js +105 -0
  91. package/dist-lib/utils/snapshotDatabase.js.map +1 -0
  92. package/dist-lib/utils.js +24 -22
  93. package/dist-lib/utils.js.map +1 -1
  94. package/package.json +1 -1
  95. package/src/@types/backups.ts +1 -1
  96. package/src/components/Plan/BackupEvents/BackupEvents.tsx +5 -3
  97. package/src/components/Plan/Backups/Backups.tsx +40 -1
  98. package/src/components/Plan/PlanPruneModal/PlanPruneModal.tsx +54 -11
  99. package/src/components/Plan/PlanSettings/PlanPruneSettings.tsx +145 -61
  100. package/src/components/Plan/PlanSettings/PlanSettings.module.scss +5 -0
  101. package/src/components/Plan/SnapshotViewer/SnapshotViewer.tsx +344 -0
  102. package/src/components/Plan/SnapshotViewer/SnapshotViewerFile.tsx +89 -0
  103. package/src/components/Restore/RestoreFileSelector/RestoreFileSelector.tsx +82 -145
  104. package/src/components/Restore/RestoredFileBrowser/RestoredFileBrowser.tsx +71 -156
  105. package/src/components/common/SnapshotBrowser/SnapshotBrowser.module.scss +376 -0
  106. package/src/components/common/SnapshotBrowser/SnapshotBrowserDirectories.tsx +84 -0
  107. package/src/components/common/SnapshotBrowser/SnapshotBrowserFileList.tsx +52 -0
  108. package/src/components/common/SnapshotBrowser/SnapshotBrowserFileRow.tsx +44 -0
  109. package/src/components/common/SnapshotBrowser/SnapshotBrowserGoUpRow.tsx +22 -0
  110. package/src/components/common/SnapshotBrowser/SnapshotBrowserToolbar.tsx +29 -0
  111. package/src/components/common/SnapshotBrowser/hooks/useSnapshotDatabase.ts +130 -0
  112. package/src/components/common/SnapshotBrowser/hooks/useSnapshotNavigation.ts +154 -0
  113. package/src/components/common/SnapshotBrowser/hooks/useSnapshotSort.ts +24 -0
  114. package/src/components/common/SnapshotBrowser/index.ts +13 -0
  115. package/src/components/common/StatusLabel/StatusLabel.tsx +7 -1
  116. package/src/components/common/form/NumberInput/NumberInput.module.scss +1 -0
  117. package/src/components/index.ts +13 -0
  118. package/src/hooks/usePlanSingleActions.tsx +5 -3
  119. package/src/services/backups.ts +12 -0
  120. package/src/utils/index.ts +1 -0
  121. package/src/utils/snapshotDatabase.ts +201 -0
@@ -7,4 +7,5 @@ export * from './plans';
7
7
  export * from './progressHelpers';
8
8
  export * from './restore';
9
9
  export * from './getIconNameforFile';
10
+ export { default as SnapshotDatabase } from './snapshotDatabase';
10
11
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,cAAc,aAAa,CAAC;AAC5B,cAAc,WAAW,CAAC;AAC1B,cAAc,SAAS,CAAC;AACxB,cAAc,mBAAmB,CAAC;AAClC,cAAc,WAAW,CAAC;AAC1B,cAAc,sBAAsB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,cAAc,aAAa,CAAC;AAC5B,cAAc,WAAW,CAAC;AAC1B,cAAc,SAAS,CAAC;AACxB,cAAc,mBAAmB,CAAC;AAClC,cAAc,WAAW,CAAC;AAC1B,cAAc,sBAAsB,CAAC;AACrC,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,oBAAoB,CAAC"}
@@ -0,0 +1,16 @@
1
+ import { FileItem } from '../@types/system';
2
+ declare class SnapshotDatabase {
3
+ private backupId;
4
+ private db;
5
+ private isInitialized;
6
+ constructor(backupId: string);
7
+ initialize(): Promise<void>;
8
+ initializeFromFiles(files: FileItem[]): Promise<void>;
9
+ getTopLevelDirectories(maxDepth?: number): Promise<string[]>;
10
+ hasSubdirectories(dirPath: string): Promise<boolean>;
11
+ getSubdirectories(parentPath: string): Promise<string[]>;
12
+ getDirectoryContents(dirPath: string, sortField?: string, sortDirection?: string, searchTerm?: string): Promise<FileItem[]>;
13
+ cleanup(): Promise<void>;
14
+ }
15
+ export default SnapshotDatabase;
16
+ //# sourceMappingURL=snapshotDatabase.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"snapshotDatabase.d.ts","sourceRoot":"","sources":["../../src/utils/snapshotDatabase.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAoB5C,cAAM,gBAAgB;IAIP,OAAO,CAAC,QAAQ;IAH5B,OAAO,CAAC,EAAE,CAAa;IACvB,OAAO,CAAC,aAAa,CAAS;gBAEV,QAAQ,EAAE,MAAM;IAE9B,UAAU;IAaV,mBAAmB,CAAC,KAAK,EAAE,QAAQ,EAAE;IAyCrC,sBAAsB,CAAC,QAAQ,GAAE,MAAU,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAoB/D,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAkBpD,iBAAiB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAoBxD,oBAAoB,CACvB,OAAO,EAAE,MAAM,EACf,SAAS,GAAE,MAAe,EAC1B,aAAa,GAAE,MAAc,EAC7B,UAAU,GAAE,MAAW,GACvB,OAAO,CAAC,QAAQ,EAAE,CAAC;IAkDhB,OAAO;CAKf;AAED,eAAe,gBAAgB,CAAC"}
@@ -0,0 +1,105 @@
1
+ var f = Object.defineProperty;
2
+ var g = (c, i, e) => i in c ? f(c, i, { enumerable: !0, configurable: !0, writable: !0, value: e }) : c[i] = e;
3
+ var d = (c, i, e) => g(c, typeof i != "symbol" ? i + "" : i, e);
4
+ let h = null;
5
+ async function p() {
6
+ return h || (h = await import("dexie")), h;
7
+ }
8
+ class b {
9
+ constructor(i) {
10
+ d(this, "db", null);
11
+ d(this, "isInitialized", !1);
12
+ this.backupId = i;
13
+ }
14
+ async initialize() {
15
+ if (this.isInitialized) return;
16
+ const { Dexie: i } = await p();
17
+ this.db = new i(`SnapshotDB_${this.backupId}`), this.db.version(1).stores({
18
+ files: "++id, path, parentPath, fileName, isDirFlag, size, modifiedAt, depth"
19
+ }), this.isInitialized = !0;
20
+ }
21
+ async initializeFromFiles(i) {
22
+ await this.initialize(), await this.db.files.clear();
23
+ const e = 1e3, t = [];
24
+ for (let r = 0; r < i.length; r++) {
25
+ const s = i[r], l = s.path.lastIndexOf("/"), a = s.path.substring(0, l), n = (s.name || s.path.split("/").pop() || "").toLowerCase(), o = s.path.split("/").filter(Boolean).length, u = s.isDirectory ? 1 : 0;
26
+ t.push({
27
+ ...s,
28
+ parentPath: a,
29
+ fileName: n,
30
+ depth: o,
31
+ isDirFlag: u
32
+ }), t.length >= e && (await this.db.files.bulkAdd(t), t.length = 0);
33
+ }
34
+ t.length > 0 && await this.db.files.bulkAdd(t), console.log("Database initialized with", await this.db.files.count(), "total records");
35
+ }
36
+ // Get only top-level directories (depth <= maxDepth)
37
+ async getTopLevelDirectories(i = 3) {
38
+ this.isInitialized || await this.initialize();
39
+ try {
40
+ const t = (await this.db.files.where("isDirFlag").equals(1).and((r) => r.depth <= i).toArray()).map((r) => r.path);
41
+ return console.log(`getTopLevelDirectories (depth <= ${i}) returning:`, t.length, "directories"), t;
42
+ } catch (e) {
43
+ return console.error("Error getting top-level directories:", e), [];
44
+ }
45
+ }
46
+ // Check if a directory has subdirectories
47
+ async hasSubdirectories(i) {
48
+ this.isInitialized || await this.initialize();
49
+ try {
50
+ return await this.db.files.where("parentPath").equals(i).and((t) => t.isDirFlag === 1).count() > 0;
51
+ } catch (e) {
52
+ return console.error("Error checking subdirectories:", e), !1;
53
+ }
54
+ }
55
+ // Get immediate subdirectories of a given directory
56
+ async getSubdirectories(i) {
57
+ this.isInitialized || await this.initialize();
58
+ try {
59
+ const t = (await this.db.files.where("parentPath").equals(i).and((r) => r.isDirFlag === 1).toArray()).map((r) => r.path);
60
+ return console.log(`getSubdirectories for ${i}:`, t.length, "subdirectories"), t;
61
+ } catch (e) {
62
+ return console.error("Error getting subdirectories:", e), [];
63
+ }
64
+ }
65
+ // Get directory contents (files and folders in a specific directory)
66
+ async getDirectoryContents(i, e = "name", t = "asc", r = "") {
67
+ this.isInitialized || await this.initialize();
68
+ try {
69
+ let s;
70
+ if (r.trim()) {
71
+ const l = await this.db.files.where("parentPath").equals(i).toArray(), a = r.toLowerCase();
72
+ s = l.filter((n) => n.fileName.includes(a) || n.path.toLowerCase().includes(a));
73
+ } else
74
+ s = await this.db.files.where("parentPath").equals(i).toArray();
75
+ return s.sort((l, a) => {
76
+ if (l.isDirFlag && !a.isDirFlag) return -1;
77
+ if (!l.isDirFlag && a.isDirFlag) return 1;
78
+ let n, o;
79
+ switch (e) {
80
+ case "name":
81
+ n = l.fileName, o = a.fileName;
82
+ break;
83
+ case "modifiedAt":
84
+ n = new Date(l.modifiedAt).getTime(), o = new Date(a.modifiedAt).getTime();
85
+ break;
86
+ case "size":
87
+ n = l.size || 0, o = a.size || 0;
88
+ break;
89
+ default:
90
+ return 0;
91
+ }
92
+ return n < o ? t === "asc" ? -1 : 1 : n > o ? t === "asc" ? 1 : -1 : 0;
93
+ }), s;
94
+ } catch (s) {
95
+ return console.error("Error getting directory contents:", s), [];
96
+ }
97
+ }
98
+ async cleanup() {
99
+ this.db && await this.db.delete();
100
+ }
101
+ }
102
+ export {
103
+ b as default
104
+ };
105
+ //# sourceMappingURL=snapshotDatabase.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"snapshotDatabase.js","sources":["../../src/utils/snapshotDatabase.ts"],"sourcesContent":["import { FileItem } from '../@types/system';\r\n\r\n// Lazy load Dexie only when needed\r\nlet DexieModule: any = null;\r\n\r\nasync function loadDexie() {\r\n if (!DexieModule) {\r\n DexieModule = await import('dexie');\r\n }\r\n return DexieModule;\r\n}\r\n\r\ninterface FileRecord extends FileItem {\r\n id?: number;\r\n parentPath: string;\r\n fileName: string;\r\n depth: number;\r\n isDirFlag: number; // 1 for directory, 0 for file\r\n}\r\n\r\nclass SnapshotDatabase {\r\n private db: any = null;\r\n private isInitialized = false;\r\n\r\n constructor(private backupId: string) {}\r\n\r\n async initialize() {\r\n if (this.isInitialized) return;\r\n\r\n const { Dexie } = await loadDexie();\r\n\r\n this.db = new Dexie(`SnapshotDB_${this.backupId}`);\r\n this.db.version(1).stores({\r\n files: '++id, path, parentPath, fileName, isDirFlag, size, modifiedAt, depth',\r\n });\r\n\r\n this.isInitialized = true;\r\n }\r\n\r\n async initializeFromFiles(files: FileItem[]) {\r\n await this.initialize();\r\n\r\n // Clear existing data\r\n await this.db.files.clear();\r\n\r\n // Process and insert files in batches\r\n const batchSize = 1000;\r\n const records: FileRecord[] = [];\r\n\r\n for (let i = 0; i < files.length; i++) {\r\n const file = files[i];\r\n const lastSlashIndex = file.path.lastIndexOf('/');\r\n const parentPath = file.path.substring(0, lastSlashIndex);\r\n const fileName = (file.name || file.path.split('/').pop() || '').toLowerCase();\r\n const depth = file.path.split('/').filter(Boolean).length;\r\n\r\n const isDirFlag = file.isDirectory ? 1 : 0;\r\n\r\n records.push({\r\n ...file,\r\n parentPath,\r\n fileName,\r\n depth,\r\n isDirFlag,\r\n });\r\n\r\n if (records.length >= batchSize) {\r\n await this.db.files.bulkAdd(records);\r\n records.length = 0;\r\n }\r\n }\r\n\r\n if (records.length > 0) {\r\n await this.db.files.bulkAdd(records);\r\n }\r\n\r\n console.log('Database initialized with', await this.db.files.count(), 'total records');\r\n }\r\n\r\n // Get only top-level directories (depth <= maxDepth)\r\n async getTopLevelDirectories(maxDepth: number = 3): Promise<string[]> {\r\n if (!this.isInitialized) await this.initialize();\r\n\r\n try {\r\n const dirRecords = await this.db.files\r\n .where('isDirFlag')\r\n .equals(1)\r\n .and((record: FileRecord) => record.depth <= maxDepth)\r\n .toArray();\r\n\r\n const paths = dirRecords.map((record: FileRecord) => record.path);\r\n console.log(`getTopLevelDirectories (depth <= ${maxDepth}) returning:`, paths.length, 'directories');\r\n return paths;\r\n } catch (error) {\r\n console.error('Error getting top-level directories:', error);\r\n return [];\r\n }\r\n }\r\n\r\n // Check if a directory has subdirectories\r\n async hasSubdirectories(dirPath: string): Promise<boolean> {\r\n if (!this.isInitialized) await this.initialize();\r\n\r\n try {\r\n const count = await this.db.files\r\n .where('parentPath')\r\n .equals(dirPath)\r\n .and((record: FileRecord) => record.isDirFlag === 1)\r\n .count();\r\n\r\n return count > 0;\r\n } catch (error) {\r\n console.error('Error checking subdirectories:', error);\r\n return false;\r\n }\r\n }\r\n\r\n // Get immediate subdirectories of a given directory\r\n async getSubdirectories(parentPath: string): Promise<string[]> {\r\n if (!this.isInitialized) await this.initialize();\r\n\r\n try {\r\n const subdirs = await this.db.files\r\n .where('parentPath')\r\n .equals(parentPath)\r\n .and((record: FileRecord) => record.isDirFlag === 1)\r\n .toArray();\r\n\r\n const paths = subdirs.map((record: FileRecord) => record.path);\r\n console.log(`getSubdirectories for ${parentPath}:`, paths.length, 'subdirectories');\r\n return paths;\r\n } catch (error) {\r\n console.error('Error getting subdirectories:', error);\r\n return [];\r\n }\r\n }\r\n\r\n // Get directory contents (files and folders in a specific directory)\r\n async getDirectoryContents(\r\n dirPath: string,\r\n sortField: string = 'name',\r\n sortDirection: string = 'asc',\r\n searchTerm: string = '',\r\n ): Promise<FileItem[]> {\r\n if (!this.isInitialized) await this.initialize();\r\n\r\n try {\r\n let results: FileRecord[];\r\n\r\n if (searchTerm.trim()) {\r\n const allInDir = await this.db.files.where('parentPath').equals(dirPath).toArray();\r\n const lowerSearch = searchTerm.toLowerCase();\r\n results = allInDir.filter((item: FileRecord) => item.fileName.includes(lowerSearch) || item.path.toLowerCase().includes(lowerSearch));\r\n } else {\r\n results = await this.db.files.where('parentPath').equals(dirPath).toArray();\r\n }\r\n\r\n // Sort results\r\n results.sort((a: FileRecord, b: FileRecord) => {\r\n // Directories first\r\n if (a.isDirFlag && !b.isDirFlag) return -1;\r\n if (!a.isDirFlag && b.isDirFlag) return 1;\r\n\r\n let aVal: any, bVal: any;\r\n switch (sortField) {\r\n case 'name':\r\n aVal = a.fileName;\r\n bVal = b.fileName;\r\n break;\r\n case 'modifiedAt':\r\n aVal = new Date(a.modifiedAt).getTime();\r\n bVal = new Date(b.modifiedAt).getTime();\r\n break;\r\n case 'size':\r\n aVal = a.size || 0;\r\n bVal = b.size || 0;\r\n break;\r\n default:\r\n return 0;\r\n }\r\n\r\n if (aVal < bVal) return sortDirection === 'asc' ? -1 : 1;\r\n if (aVal > bVal) return sortDirection === 'asc' ? 1 : -1;\r\n return 0;\r\n });\r\n\r\n return results;\r\n } catch (error) {\r\n console.error('Error getting directory contents:', error);\r\n return [];\r\n }\r\n }\r\n\r\n async cleanup() {\r\n if (this.db) {\r\n await this.db.delete();\r\n }\r\n }\r\n}\r\n\r\nexport default SnapshotDatabase;\r\n"],"names":["DexieModule","loadDexie","SnapshotDatabase","backupId","__publicField","Dexie","files","batchSize","records","i","file","lastSlashIndex","parentPath","fileName","depth","isDirFlag","maxDepth","paths","record","error","dirPath","sortField","sortDirection","searchTerm","results","allInDir","lowerSearch","item","a","b","aVal","bVal"],"mappings":";;;AAGA,IAAIA,IAAmB;AAEvB,eAAeC,IAAY;AACxB,SAAKD,MACFA,IAAc,MAAM,OAAO,OAAO,IAE9BA;AACV;AAUA,MAAME,EAAiB;AAAA,EAIpB,YAAoBC,GAAkB;AAH9B,IAAAC,EAAA,YAAU;AACV,IAAAA,EAAA,uBAAgB;AAEJ,SAAA,WAAAD;AAAA,EAAmB;AAAA,EAEvC,MAAM,aAAa;AAChB,QAAI,KAAK,cAAe;AAExB,UAAM,EAAE,OAAAE,MAAU,MAAMJ,EAAA;AAExB,SAAK,KAAK,IAAII,EAAM,cAAc,KAAK,QAAQ,EAAE,GACjD,KAAK,GAAG,QAAQ,CAAC,EAAE,OAAO;AAAA,MACvB,OAAO;AAAA,IAAA,CACT,GAED,KAAK,gBAAgB;AAAA,EACxB;AAAA,EAEA,MAAM,oBAAoBC,GAAmB;AAC1C,UAAM,KAAK,WAAA,GAGX,MAAM,KAAK,GAAG,MAAM,MAAA;AAGpB,UAAMC,IAAY,KACZC,IAAwB,CAAA;AAE9B,aAASC,IAAI,GAAGA,IAAIH,EAAM,QAAQG,KAAK;AACpC,YAAMC,IAAOJ,EAAMG,CAAC,GACdE,IAAiBD,EAAK,KAAK,YAAY,GAAG,GAC1CE,IAAaF,EAAK,KAAK,UAAU,GAAGC,CAAc,GAClDE,KAAYH,EAAK,QAAQA,EAAK,KAAK,MAAM,GAAG,EAAE,SAAS,IAAI,YAAA,GAC3DI,IAAQJ,EAAK,KAAK,MAAM,GAAG,EAAE,OAAO,OAAO,EAAE,QAE7CK,IAAYL,EAAK,cAAc,IAAI;AAEzC,MAAAF,EAAQ,KAAK;AAAA,QACV,GAAGE;AAAA,QACH,YAAAE;AAAA,QACA,UAAAC;AAAA,QACA,OAAAC;AAAA,QACA,WAAAC;AAAA,MAAA,CACF,GAEGP,EAAQ,UAAUD,MACnB,MAAM,KAAK,GAAG,MAAM,QAAQC,CAAO,GACnCA,EAAQ,SAAS;AAAA,IAEvB;AAEA,IAAIA,EAAQ,SAAS,KAClB,MAAM,KAAK,GAAG,MAAM,QAAQA,CAAO,GAGtC,QAAQ,IAAI,6BAA6B,MAAM,KAAK,GAAG,MAAM,MAAA,GAAS,eAAe;AAAA,EACxF;AAAA;AAAA,EAGA,MAAM,uBAAuBQ,IAAmB,GAAsB;AACnE,IAAK,KAAK,iBAAe,MAAM,KAAK,WAAA;AAEpC,QAAI;AAOD,YAAMC,KANa,MAAM,KAAK,GAAG,MAC7B,MAAM,WAAW,EACjB,OAAO,CAAC,EACR,IAAI,CAACC,MAAuBA,EAAO,SAASF,CAAQ,EACpD,QAAA,GAEqB,IAAI,CAACE,MAAuBA,EAAO,IAAI;AAChE,qBAAQ,IAAI,oCAAoCF,CAAQ,gBAAgBC,EAAM,QAAQ,aAAa,GAC5FA;AAAA,IACV,SAASE,GAAO;AACb,qBAAQ,MAAM,wCAAwCA,CAAK,GACpD,CAAA;AAAA,IACV;AAAA,EACH;AAAA;AAAA,EAGA,MAAM,kBAAkBC,GAAmC;AACxD,IAAK,KAAK,iBAAe,MAAM,KAAK,WAAA;AAEpC,QAAI;AAOD,aANc,MAAM,KAAK,GAAG,MACxB,MAAM,YAAY,EAClB,OAAOA,CAAO,EACd,IAAI,CAACF,MAAuBA,EAAO,cAAc,CAAC,EAClD,MAAA,IAEW;AAAA,IAClB,SAASC,GAAO;AACb,qBAAQ,MAAM,kCAAkCA,CAAK,GAC9C;AAAA,IACV;AAAA,EACH;AAAA;AAAA,EAGA,MAAM,kBAAkBP,GAAuC;AAC5D,IAAK,KAAK,iBAAe,MAAM,KAAK,WAAA;AAEpC,QAAI;AAOD,YAAMK,KANU,MAAM,KAAK,GAAG,MAC1B,MAAM,YAAY,EAClB,OAAOL,CAAU,EACjB,IAAI,CAACM,MAAuBA,EAAO,cAAc,CAAC,EAClD,QAAA,GAEkB,IAAI,CAACA,MAAuBA,EAAO,IAAI;AAC7D,qBAAQ,IAAI,yBAAyBN,CAAU,KAAKK,EAAM,QAAQ,gBAAgB,GAC3EA;AAAA,IACV,SAASE,GAAO;AACb,qBAAQ,MAAM,iCAAiCA,CAAK,GAC7C,CAAA;AAAA,IACV;AAAA,EACH;AAAA;AAAA,EAGA,MAAM,qBACHC,GACAC,IAAoB,QACpBC,IAAwB,OACxBC,IAAqB,IACD;AACpB,IAAK,KAAK,iBAAe,MAAM,KAAK,WAAA;AAEpC,QAAI;AACD,UAAIC;AAEJ,UAAID,EAAW,QAAQ;AACpB,cAAME,IAAW,MAAM,KAAK,GAAG,MAAM,MAAM,YAAY,EAAE,OAAOL,CAAO,EAAE,QAAA,GACnEM,IAAcH,EAAW,YAAA;AAC/B,QAAAC,IAAUC,EAAS,OAAO,CAACE,MAAqBA,EAAK,SAAS,SAASD,CAAW,KAAKC,EAAK,KAAK,YAAA,EAAc,SAASD,CAAW,CAAC;AAAA,MACvI;AACG,QAAAF,IAAU,MAAM,KAAK,GAAG,MAAM,MAAM,YAAY,EAAE,OAAOJ,CAAO,EAAE,QAAA;AAIrE,aAAAI,EAAQ,KAAK,CAACI,GAAeC,MAAkB;AAE5C,YAAID,EAAE,aAAa,CAACC,EAAE,UAAW,QAAO;AACxC,YAAI,CAACD,EAAE,aAAaC,EAAE,UAAW,QAAO;AAExC,YAAIC,GAAWC;AACf,gBAAQV,GAAA;AAAA,UACL,KAAK;AACF,YAAAS,IAAOF,EAAE,UACTG,IAAOF,EAAE;AACT;AAAA,UACH,KAAK;AACF,YAAAC,IAAO,IAAI,KAAKF,EAAE,UAAU,EAAE,QAAA,GAC9BG,IAAO,IAAI,KAAKF,EAAE,UAAU,EAAE,QAAA;AAC9B;AAAA,UACH,KAAK;AACF,YAAAC,IAAOF,EAAE,QAAQ,GACjBG,IAAOF,EAAE,QAAQ;AACjB;AAAA,UACH;AACG,mBAAO;AAAA,QAAA;AAGb,eAAIC,IAAOC,IAAaT,MAAkB,QAAQ,KAAK,IACnDQ,IAAOC,IAAaT,MAAkB,QAAQ,IAAI,KAC/C;AAAA,MACV,CAAC,GAEME;AAAA,IACV,SAASL,GAAO;AACb,qBAAQ,MAAM,qCAAqCA,CAAK,GACjD,CAAA;AAAA,IACV;AAAA,EACH;AAAA,EAEA,MAAM,UAAU;AACb,IAAI,KAAK,MACN,MAAM,KAAK,GAAG,OAAA;AAAA,EAEpB;AACH;"}
package/dist-lib/utils.js CHANGED
@@ -1,48 +1,50 @@
1
- import { API_URL as r, APP_NAME as a, DEFAULT_PLAN_SETTINGS as o, DEV_MODE as s } from "./utils/constants.js";
2
- import { calculateDirectorySizes as g, clipPath as n, compareVersions as l, formatBytes as m, formatDateTime as c, formatDuration as p, formatNumberToK as P, formatPermissions as f, formatSeconds as A, getAvailableCliApps as M, getLogLevelName as d, getOSIcon as D, getProcessorIcon as E, getUpdateDocLink as S, isMobile as I, isServerEdition as N, isValidEmail as u, isValidURL as v, secondsToMinutes as x, shouldDisplayStorageField as L, sortFileItems as h, timeAgo as T } from "./utils/helpers.js";
1
+ import { API_URL as a, APP_NAME as r, DEFAULT_PLAN_SETTINGS as o, DEV_MODE as s } from "./utils/constants.js";
2
+ import { calculateDirectorySizes as n, clipPath as g, compareVersions as l, formatBytes as m, formatDateTime as p, formatDuration as c, formatNumberToK as P, formatPermissions as f, formatSeconds as A, getAvailableCliApps as d, getLogLevelName as D, getOSIcon as M, getProcessorIcon as S, getUpdateDocLink as E, isMobile as u, isServerEdition as x, isValidEmail as I, isValidURL as N, secondsToMinutes as h, shouldDisplayStorageField as v, sortFileItems as L, timeAgo as T } from "./utils/helpers.js";
3
3
  import { isPlanSettingsValid as R, planIntervalAgeName as V, planIntervalName as _ } from "./utils/plans.js";
4
- import { extractResticData as b, generateBackupProgressMessage as k, generateMirrorProgressMessage as y, generateRestoreProgressMessage as B, getBackupEventActionMessage as z, getRestoreEventActionMessage as O } from "./utils/progressHelpers.js";
4
+ import { extractResticData as U, generateBackupProgressMessage as k, generateMirrorProgressMessage as y, generateRestoreProgressMessage as B, getBackupEventActionMessage as z, getRestoreEventActionMessage as O } from "./utils/progressHelpers.js";
5
5
  import { getParentPath as G, getPathSeparator as K, normalizePath as j, splitPath as q } from "./utils/restore.js";
6
6
  import { getIconNameForFile as H } from "./utils/getIconNameforFile.js";
7
+ import { default as Q } from "./utils/snapshotDatabase.js";
7
8
  export {
8
- r as API_URL,
9
- a as APP_NAME,
9
+ a as API_URL,
10
+ r as APP_NAME,
10
11
  o as DEFAULT_PLAN_SETTINGS,
11
12
  s as DEV_MODE,
12
- g as calculateDirectorySizes,
13
- n as clipPath,
13
+ Q as SnapshotDatabase,
14
+ n as calculateDirectorySizes,
15
+ g as clipPath,
14
16
  l as compareVersions,
15
- b as extractResticData,
17
+ U as extractResticData,
16
18
  m as formatBytes,
17
- c as formatDateTime,
18
- p as formatDuration,
19
+ p as formatDateTime,
20
+ c as formatDuration,
19
21
  P as formatNumberToK,
20
22
  f as formatPermissions,
21
23
  A as formatSeconds,
22
24
  k as generateBackupProgressMessage,
23
25
  y as generateMirrorProgressMessage,
24
26
  B as generateRestoreProgressMessage,
25
- M as getAvailableCliApps,
27
+ d as getAvailableCliApps,
26
28
  z as getBackupEventActionMessage,
27
29
  H as getIconNameForFile,
28
- d as getLogLevelName,
29
- D as getOSIcon,
30
+ D as getLogLevelName,
31
+ M as getOSIcon,
30
32
  G as getParentPath,
31
33
  K as getPathSeparator,
32
- E as getProcessorIcon,
34
+ S as getProcessorIcon,
33
35
  O as getRestoreEventActionMessage,
34
- S as getUpdateDocLink,
35
- I as isMobile,
36
+ E as getUpdateDocLink,
37
+ u as isMobile,
36
38
  R as isPlanSettingsValid,
37
- N as isServerEdition,
38
- u as isValidEmail,
39
- v as isValidURL,
39
+ x as isServerEdition,
40
+ I as isValidEmail,
41
+ N as isValidURL,
40
42
  j as normalizePath,
41
43
  V as planIntervalAgeName,
42
44
  _ as planIntervalName,
43
- x as secondsToMinutes,
44
- L as shouldDisplayStorageField,
45
- h as sortFileItems,
45
+ h as secondsToMinutes,
46
+ v as shouldDisplayStorageField,
47
+ L as sortFileItems,
46
48
  q as splitPath,
47
49
  T as timeAgo
48
50
  };
@@ -1 +1 @@
1
- {"version":3,"file":"utils.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;"}
1
+ {"version":3,"file":"utils.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;"}
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.24",
4
+ "version": "0.1.26",
5
5
  "author": "Plutonhq",
6
6
  "license": "Apache-2.0",
7
7
  "publishConfig": {
@@ -55,7 +55,7 @@ export type Backup = {
55
55
  started: number;
56
56
  ended: number;
57
57
  duration: number;
58
- status: 'completed' | 'cancelled' | 'failed' | 'started';
58
+ status: 'completed' | 'cancelled' | 'failed' | 'started' | 'retrying' | 'initializing';
59
59
  inProgress: boolean;
60
60
  totalFiles: number;
61
61
  totalSize: number;
@@ -57,9 +57,11 @@ const BackupEvents = ({ id, type = 'backup', sourceId, sourceType, planId, inPro
57
57
  <Icon type={inProgress ? 'loading' : hasFinished === 'Completed' ? 'check-circle-filled' : 'error-circle-filled'} size={14} />{' '}
58
58
  {inProgress ? `${type} In Progress` : `${type} ${hasFinished}` || 'Unknown'}
59
59
  </div>
60
- <div title="Duration" className={classes.duration}>
61
- <Icon type="clock" size={14} /> {(progressDataToUse?.duration && formatDuration(progressDataToUse.duration / 1000)) || 'N/A'}
62
- </div>
60
+ {!inProgress && hasFinished && (
61
+ <div title="Duration" className={classes.duration}>
62
+ <Icon type="clock" size={14} /> {(progressDataToUse?.duration && formatDuration(progressDataToUse.duration / 1000)) || 'N/A'}
63
+ </div>
64
+ )}
63
65
  </div>
64
66
  {isLoading && (
65
67
  <div className={classes.loading}>
@@ -6,7 +6,14 @@ import { formatBytes, formatDateTime, formatDuration, formatNumberToK, timeAgo }
6
6
  import Icon from '../../common/Icon/Icon';
7
7
  import classes from './Backups.module.scss';
8
8
  import ActionModal from '../../common/ActionModal/ActionModal';
9
- import { useCancelBackupDownload, useDeleteBackup, useDownloadBackup, useGetBackupDownload, useUpdateBackup } from '../../../services/backups';
9
+ import {
10
+ useCancelBackupDownload,
11
+ useDeleteBackup,
12
+ useDownloadBackup,
13
+ useGetBackupDownload,
14
+ useUpdateBackup,
15
+ useBrowseSnapshot,
16
+ } from '../../../services/backups';
10
17
  import { useQueryClient } from '@tanstack/react-query';
11
18
  import RestoreWizard from '../../Restore/RestoreWizard/RestoreWizard';
12
19
  import StatusLabel from '../../common/StatusLabel/StatusLabel';
@@ -14,6 +21,7 @@ import BackupEvents from '../BackupEvents/BackupEvents';
14
21
  import Input from '../../common/form/Input/Input';
15
22
  import MirrorStatusBadge from '../Mirrors/MirrorStatusBadge';
16
23
  import MirrorStorageSelectorModal from '../Mirrors/MirrorStorageSelectorModal';
24
+ import { SidePanel, SnapshotViewer } from '../..';
17
25
 
18
26
  const DownloadLabel = ({ download, downloadBackup }: { download: Backup['download']; downloadBackup: () => void }) => {
19
27
  if (download?.status === 'started') {
@@ -92,6 +100,7 @@ const Backups = ({
92
100
  const [showDeleteModal, setShowDeleteModal] = useState<Backup | false>(false);
93
101
  const [showRestoreModal, setShowRestoreModal] = useState<Backup | false>(false);
94
102
  const [showBackupEvents, setShowBackupEvents] = useState<false | string>(false);
103
+ const [showSnapshotViewer, setShowSnapshotViewer] = useState<Backup | false>(false);
95
104
  const [showEditModal, setShowEditModal] = useState<Backup | false>(false);
96
105
  const [showStorageSelector, setShowStorageSelector] = useState<Backup | false>(false);
97
106
  const queryClient = useQueryClient();
@@ -100,8 +109,11 @@ const Backups = ({
100
109
  const downloadBackupMutation = useDownloadBackup();
101
110
  const cancelDownloadMutation = useCancelBackupDownload();
102
111
  const getDownloadMutation = useGetBackupDownload();
112
+ const browseSnapshotMutation = useBrowseSnapshot();
103
113
  const isSync = method === 'sync';
104
114
 
115
+ const snapshotFiles = browseSnapshotMutation.data?.result;
116
+
105
117
  console.log('backups :', backups);
106
118
 
107
119
  const removeBackup = (backup: Backup) => {
@@ -289,6 +301,20 @@ const Backups = ({
289
301
  <Icon type={isDownloading ? 'close' : 'download'} size={14} /> {isDownloading ? 'Cancel Download' : 'Download'}
290
302
  </button>
291
303
  )}
304
+ {!isSync && status === 'completed' && active && (
305
+ <button
306
+ onClick={() => {
307
+ toast.promise(browseSnapshotMutation.mutateAsync({ backupId: id }), {
308
+ pending: 'Getting Snapshot Data...',
309
+ error: 'Failed to Get Snapshot Data.',
310
+ });
311
+ setShowSnapOptions(false);
312
+ setShowSnapshotViewer(snapshot);
313
+ }}
314
+ >
315
+ <Icon type="folders" size={14} /> Browse
316
+ </button>
317
+ )}
292
318
  {status === 'completed' && active && (
293
319
  <button
294
320
  className={isDownloading ? 'notAllowed' : ''}
@@ -414,6 +440,19 @@ const Backups = ({
414
440
  onClose={() => setShowStorageSelector(false)}
415
441
  />
416
442
  )}
443
+ {showSnapshotViewer && showSnapshotViewer.id && browseSnapshotMutation.isSuccess && (
444
+ <SidePanel width="100%" title="Browse Snapshot" icon={<Icon type={'folders'} size={20} />} close={() => setShowSnapshotViewer(false)}>
445
+ <SnapshotViewer
446
+ files={snapshotFiles || []}
447
+ backup={showSnapshotViewer}
448
+ planId={planId}
449
+ isSync={isSync}
450
+ onClose={() => setShowSnapshotViewer(false)}
451
+ mirrors={replicationSettings?.enabled ? showSnapshotViewer.mirrors : undefined}
452
+ primaryStorage={replicationSettings?.enabled ? storage : undefined}
453
+ />
454
+ </SidePanel>
455
+ )}
417
456
  </div>
418
457
  );
419
458
  };
@@ -15,6 +15,56 @@ interface PlanPruneModalProps {
15
15
  const PlanPruneModal = ({ planId, method, prune, snapshotsCount, taskPending, close }: PlanPruneModalProps) => {
16
16
  const pruneMutation = usePrunePlan();
17
17
 
18
+ const getBackupMessage = () => {
19
+ switch (prune.policy) {
20
+ case 'keepLast':
21
+ return (
22
+ <>
23
+ Your plan is set to keep the last {prune.snapCount} backups.{' '}
24
+ {prune.snapCount < snapshotsCount
25
+ ? `Running prune now will remove ${snapshotsCount - prune.snapCount} older snapshots, freeing up some storage space.`
26
+ : 'There are no excess snapshots to clean up.'}
27
+ </>
28
+ );
29
+ case 'forgetByAge':
30
+ return (
31
+ <>
32
+ Your plan is set to remove backups older than {planIntervalAgeName(prune.forgetAge || '3m')}, while always keeping at least{' '}
33
+ {prune.snapCount || 1} backup{(prune.snapCount || 1) > 1 ? 's' : ''}. This action will remove any backups that exceed the retention
34
+ policy.
35
+ </>
36
+ );
37
+ case 'custom':
38
+ return (
39
+ <>
40
+ Your plan uses an advanced retention policy
41
+ {prune.keepDailySnaps ? `, keeping daily backups for ${prune.keepDailySnaps} days` : ''}
42
+ {prune.keepWeeklySnaps ? `, weekly backups for ${prune.keepWeeklySnaps} weeks` : ''}
43
+ {prune.keepMonthlySnaps ? `, monthly backups for ${prune.keepMonthlySnaps} months` : ''}, while always keeping at least{' '}
44
+ {prune.snapCount || 1} backup{(prune.snapCount || 1) > 1 ? 's' : ''}. This action will remove any backups that exceed the retention
45
+ policy.
46
+ </>
47
+ );
48
+ case 'disable':
49
+ return <>Pruning is disabled for this plan. No backups will be removed.</>;
50
+ default:
51
+ return (
52
+ <>
53
+ Your plan is set to maintain {prune.snapCount} recent backups.{' '}
54
+ {prune.snapCount < snapshotsCount
55
+ ? `Running prune now will remove ${snapshotsCount - prune.snapCount} older snapshots, freeing up some storage space.`
56
+ : 'There are no excess snapshots to clean up.'}
57
+ </>
58
+ );
59
+ }
60
+ };
61
+
62
+ const canPrune = () => {
63
+ if (prune.policy === 'disable') return false;
64
+ if (prune.policy === 'keepLast' && prune.snapCount >= snapshotsCount) return false;
65
+ return true;
66
+ };
67
+
18
68
  return (
19
69
  <ActionModal
20
70
  title={method === 'sync' ? 'Clean Up Old Revisions' : 'Clean Up Old Backups'}
@@ -23,25 +73,18 @@ const PlanPruneModal = ({ planId, method, prune, snapshotsCount, taskPending, cl
23
73
  {method === 'sync' ? (
24
74
  <>
25
75
  Your plan is set to maintain{' '}
26
- {prune.policy === 'forgetByAge' ? `file revisions newer than ${planIntervalAgeName(prune.forgetAge || '3m')}.` : ''}{' '}
27
- {prune.policy === 'forgetByFileCount' ? `Only keep the last ${prune.snapCount} versions of each file.` : ''} This action will
28
- remove any excess file revisions older than the retention policy.
76
+ {prune.policy === 'forgetByAge' ? `file revisions newer than ${planIntervalAgeName(prune.forgetAge || '3m')}.` : ''} This action
77
+ will remove any excess file revisions older than the retention policy.
29
78
  </>
30
79
  ) : (
31
- <>
32
- Your plan is set to maintain {prune.snapCount} recent backups(snapshots).{' '}
33
- {prune.snapCount < snapshotsCount
34
- ? `Running prune now will remove ${' '}
35
- ${snapshotsCount - prune.snapCount} older snapshots, freeing up some storage space`
36
- : 'There are no excess snapshots to clean up.'}
37
- </>
80
+ getBackupMessage()
38
81
  )}
39
82
  </>
40
83
  }
41
84
  closeModal={() => !pruneMutation.isPending && close()}
42
85
  width="500px"
43
86
  primaryAction={{
44
- title: method === 'sync' ? `Yes, Remove Old Revisions` : prune.snapCount < snapshotsCount ? 'Yes, Remove Old Backups' : '',
87
+ title: method === 'sync' ? 'Yes, Remove Old Revisions' : canPrune() ? 'Yes, Remove Old Backups' : '',
45
88
  type: 'default',
46
89
  isPending: pruneMutation.isPending,
47
90
  action: () =>