@futo-org/backups-orchestrator-ui 0.26.0 → 0.28.0

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 (156) hide show
  1. package/dist/components/backends/BackendItem.svelte +28 -24
  2. package/dist/components/backends/BackendsList.svelte +33 -34
  3. package/dist/components/backends/BackendsList.svelte.d.ts +1 -0
  4. package/dist/components/backends/dialogs/SelectBackendModal.svelte +44 -55
  5. package/dist/components/backups/BackupItem.svelte +33 -26
  6. package/dist/components/backups/BackupStatus.svelte +200 -0
  7. package/dist/components/backups/BackupStatus.svelte.d.ts +19 -0
  8. package/dist/components/backups/BackupsList.svelte +17 -11
  9. package/dist/components/backups/dialogs/RestoreSnapshotModal.svelte +2 -2
  10. package/dist/components/backups/dialogs/SelectRepositoryModal.svelte +26 -36
  11. package/dist/components/backups/dialogs/ViewStatusModal.svelte +175 -0
  12. package/dist/components/backups/dialogs/ViewStatusModal.svelte.d.ts +8 -0
  13. package/dist/components/backups/run-history/RepositoryRunHistory.svelte +22 -10
  14. package/dist/components/backups/run-history/RepositoryRunHistory.svelte.d.ts +2 -0
  15. package/dist/components/backups/run-history/RepositoryRunHistoryItem.svelte +46 -20
  16. package/dist/components/backups/run-history/RepositoryRunHistoryPage.svelte +135 -0
  17. package/dist/components/backups/run-history/RepositoryRunHistoryPage.svelte.d.ts +8 -0
  18. package/dist/components/backups/run-history/RunHistoryModal.svelte +2 -2
  19. package/dist/components/backups/snapshots-list/RepositorySnapshotsList.svelte +22 -10
  20. package/dist/components/backups/snapshots-list/RepositorySnapshotsList.svelte.d.ts +2 -0
  21. package/dist/components/backups/snapshots-list/RepositorySnapshotsListItem.svelte +23 -21
  22. package/dist/components/backups/snapshots-list/RepositorySnapshotsPage.svelte +115 -0
  23. package/dist/components/backups/snapshots-list/RepositorySnapshotsPage.svelte.d.ts +9 -0
  24. package/dist/components/backups/snapshots-list/SnapshotsListModal.svelte +2 -2
  25. package/dist/components/dashboard/Dashboard.svelte +14 -7
  26. package/dist/components/dashboard/Dashboard.svelte.d.ts +1 -1
  27. package/dist/components/dashboard/DashboardAvgBackupTime.svelte +1 -1
  28. package/dist/components/dashboard/DashboardBackupHealth.svelte +5 -9
  29. package/dist/components/dashboard/DashboardBackupHealth.svelte.d.ts +1 -1
  30. package/dist/components/dashboard/DashboardCurrentUsage.svelte +1 -1
  31. package/dist/components/dashboard/DashboardDailyBackupTime.svelte +1 -1
  32. package/dist/components/dashboard/DashboardInstall.svelte +1 -1
  33. package/dist/components/dashboard/DashboardRecentBackups.svelte +47 -78
  34. package/dist/components/dashboard/DashboardTotalStored.svelte +1 -1
  35. package/dist/components/integrations/immich/ImmichBackupSettings.svelte +12 -0
  36. package/dist/components/integrations/immich/ImmichBackupSettings.svelte.d.ts +18 -0
  37. package/dist/components/integrations/immich/ImmichBackupsCard.svelte +73 -0
  38. package/dist/components/integrations/immich/ImmichBackupsCard.svelte.d.ts +12 -0
  39. package/dist/components/integrations/immich/ImmichBackupsNavButton.svelte +55 -0
  40. package/dist/components/integrations/immich/ImmichBackupsNavButton.svelte.d.ts +10 -0
  41. package/dist/components/integrations/immich/ImmichBackupsPage.svelte +15 -3
  42. package/dist/components/integrations/immich/ImmichBackupsPage.svelte.d.ts +7 -1
  43. package/dist/components/integrations/immich/ImmichBackupsSidebarItem.svelte +66 -0
  44. package/dist/components/integrations/immich/ImmichBackupsSidebarItem.svelte.d.ts +8 -0
  45. package/dist/components/integrations/immich/ImmichManageBackup.svelte +53 -14
  46. package/dist/components/integrations/immich/ImmichManageBackupOverview.svelte +45 -71
  47. package/dist/components/integrations/immich/ImmichOnboardingSetupFlow.svelte +123 -82
  48. package/dist/components/integrations/immich/ImmichOnboardingSetupFlow.svelte.d.ts +1 -1
  49. package/dist/components/integrations/immich/settings/ImmichSettingsBackupContents.svelte +313 -0
  50. package/dist/components/integrations/immich/settings/ImmichSettingsBackupContents.svelte.d.ts +3 -0
  51. package/dist/components/integrations/immich/settings/ImmichSettingsSchedule.svelte +218 -0
  52. package/dist/components/integrations/immich/settings/ImmichSettingsSchedule.svelte.d.ts +3 -0
  53. package/dist/components/integrations/immich/settings/ImmichSettingsStorage.svelte +207 -0
  54. package/dist/components/integrations/immich/settings/ImmichSettingsStorage.svelte.d.ts +3 -0
  55. package/dist/components/onboarding/RecoveryKeyDisplay.svelte +34 -20
  56. package/dist/components/onboarding/SampleOnboarding.svelte +57 -38
  57. package/dist/components/onboarding/stages/OnboardingStageBackupServices.svelte +10 -24
  58. package/dist/components/onboarding/steps/OnboardingStep1FinishSetup.svelte +44 -0
  59. package/dist/components/onboarding/steps/OnboardingStep1FinishSetup.svelte.d.ts +7 -0
  60. package/dist/components/onboarding/steps/OnboardingStep2ConnectAccount.svelte +50 -0
  61. package/dist/components/onboarding/steps/OnboardingStep2ConnectAccount.svelte.d.ts +7 -0
  62. package/dist/components/onboarding/steps/OnboardingStep3SaveRecoveryKey.svelte +45 -0
  63. package/dist/components/onboarding/steps/OnboardingStep3SaveRecoveryKey.svelte.d.ts +8 -0
  64. package/dist/components/onboarding/steps/OnboardingStep4FirstBackup.svelte +67 -0
  65. package/dist/components/onboarding/steps/OnboardingStep4FirstBackup.svelte.d.ts +9 -0
  66. package/dist/components/onboarding/steps/OnboardingStepLayout.svelte +58 -0
  67. package/dist/components/onboarding/steps/OnboardingStepLayout.svelte.d.ts +19 -0
  68. package/dist/components/onboarding/upsell/UpsellBackupFeaturesGrid.svelte +52 -0
  69. package/dist/components/onboarding/upsell/UpsellBackupFeaturesGrid.svelte.d.ts +18 -0
  70. package/dist/components/onboarding/upsell/UpsellExplainFutoBackups.svelte +33 -0
  71. package/dist/components/onboarding/upsell/UpsellExplainFutoBackups.svelte.d.ts +8 -0
  72. package/dist/components/onboarding/upsell/UpsellFrequentlyAskedQuestions.svelte +27 -0
  73. package/dist/components/onboarding/upsell/UpsellFrequentlyAskedQuestions.svelte.d.ts +10 -0
  74. package/dist/components/onboarding/upsell/UpsellFutoBackupsBadge.svelte +7 -0
  75. package/dist/components/onboarding/upsell/UpsellFutoBackupsBadge.svelte.d.ts +18 -0
  76. package/dist/components/onboarding/upsell/UpsellHowItWorks.svelte +46 -0
  77. package/dist/components/onboarding/upsell/UpsellHowItWorks.svelte.d.ts +6 -0
  78. package/dist/components/onboarding/upsell/UpsellModal.svelte +43 -0
  79. package/dist/components/onboarding/upsell/UpsellModal.svelte.d.ts +9 -0
  80. package/dist/components/onboarding/upsell/UpsellPage.svelte +39 -0
  81. package/dist/components/onboarding/upsell/UpsellPage.svelte.d.ts +13 -0
  82. package/dist/components/onboarding/upsell/UpsellProtectYourImmichLibrary.svelte +58 -0
  83. package/dist/components/onboarding/upsell/UpsellProtectYourImmichLibrary.svelte.d.ts +8 -0
  84. package/dist/components/schedules/RepositoryPicker.svelte +45 -38
  85. package/dist/components/schedules/ScheduleItem.svelte +32 -26
  86. package/dist/components/schedules/ScheduleList.svelte +16 -10
  87. package/dist/components/settings/GlobalSettings.svelte +8 -0
  88. package/dist/components/settings/GlobalSettings.svelte.d.ts +18 -0
  89. package/dist/components/settings/SettingsBackends.svelte +16 -0
  90. package/dist/components/settings/SettingsBackends.svelte.d.ts +18 -0
  91. package/dist/components/settings/SettingsBilling.svelte +69 -0
  92. package/dist/components/settings/SettingsBilling.svelte.d.ts +6 -0
  93. package/dist/components/settings/SettingsNotifications.svelte +47 -0
  94. package/dist/components/settings/SettingsNotifications.svelte.d.ts +6 -0
  95. package/dist/components/test/ImmichTestUi.svelte +129 -27
  96. package/dist/components/test/TestUi.svelte +4 -4
  97. package/dist/components/test/dashboard/ActiveJobs.svelte +2 -2
  98. package/dist/components/test/immich/MockImmichPhotos.svelte +36 -0
  99. package/dist/components/test/immich/MockImmichPhotos.svelte.d.ts +18 -0
  100. package/dist/components/test/immich/MockImmichPurchaseInfo.svelte +29 -0
  101. package/dist/components/test/immich/MockImmichPurchaseInfo.svelte.d.ts +18 -0
  102. package/dist/components/test/immich/MockImmichStorageSpace.svelte +37 -0
  103. package/dist/components/test/immich/MockImmichStorageSpace.svelte.d.ts +6 -0
  104. package/dist/components/ui/Accordion.svelte +67 -0
  105. package/dist/components/ui/Accordion.svelte.d.ts +11 -0
  106. package/dist/components/ui/ListToolbar.svelte +60 -0
  107. package/dist/components/ui/ListToolbar.svelte.d.ts +16 -0
  108. package/dist/components/ui/PageLayout.svelte +21 -9
  109. package/dist/components/ui/PageLayout.svelte.d.ts +1 -0
  110. package/dist/components/ui/Pagination.svelte +80 -0
  111. package/dist/components/ui/Pagination.svelte.d.ts +8 -0
  112. package/dist/components/ui/PathListField.svelte +17 -14
  113. package/dist/components/ui/StackList.svelte +18 -18
  114. package/dist/components/ui/StackList.svelte.d.ts +6 -27
  115. package/dist/components/ui/StackListItem.svelte +47 -25
  116. package/dist/components/ui/StackListItem.svelte.d.ts +5 -2
  117. package/dist/components/ui/StackListOption.svelte +33 -0
  118. package/dist/components/ui/StackListOption.svelte.d.ts +11 -0
  119. package/dist/components/ui/StackListPlaceholder.svelte +12 -0
  120. package/dist/components/ui/StackListPlaceholder.svelte.d.ts +7 -0
  121. package/dist/components/ui/StepNumber.svelte +20 -0
  122. package/dist/components/ui/StepNumber.svelte.d.ts +7 -0
  123. package/dist/components/util/Suspense.svelte +7 -3
  124. package/dist/components/util/YuccaContext.svelte +1 -1
  125. package/dist/events.js +1 -1
  126. package/dist/fetch-client.d.ts +2 -0
  127. package/dist/index.d.ts +6 -1
  128. package/dist/index.js +6 -1
  129. package/dist/services/backend.service.js +3 -0
  130. package/dist/services/immich.integration.service.d.ts +1 -1
  131. package/dist/services/immich.integration.service.js +2 -3
  132. package/dist/services/integrations.service.d.ts +1 -0
  133. package/dist/services/integrations.service.js +2 -1
  134. package/dist/services/log.service.svelte.d.ts +12 -0
  135. package/dist/services/log.service.svelte.js +15 -1
  136. package/dist/services/repository.service.js +9 -3
  137. package/dist/services/runHistory.service.js +2 -2
  138. package/dist/utils/format.d.ts +1 -1
  139. package/dist/utils/format.js +2 -2
  140. package/package.json +3 -3
  141. package/dist/components/backups/dialogs/ViewLogModal.svelte +0 -208
  142. package/dist/components/backups/dialogs/ViewLogModal.svelte.d.ts +0 -7
  143. package/dist/components/integrations/immich/ImmichConfigureBackup.svelte +0 -403
  144. package/dist/components/integrations/immich/ImmichConfigureBackup.svelte.d.ts +0 -9
  145. package/dist/components/integrations/immich/ImmichConfirmDefaultBackup.svelte +0 -80
  146. package/dist/components/integrations/immich/ImmichConfirmDefaultBackup.svelte.d.ts +0 -8
  147. package/dist/components/onboarding/stages/OnboardingStageKeyConfirm.svelte +0 -56
  148. package/dist/components/onboarding/stages/OnboardingStageKeyConfirm.svelte.d.ts +0 -9
  149. package/dist/components/onboarding/stages/OnboardingStageKeyIntro.svelte +0 -50
  150. package/dist/components/onboarding/stages/OnboardingStageKeyIntro.svelte.d.ts +0 -7
  151. package/dist/components/onboarding/stages/OnboardingStageKeySave.svelte +0 -44
  152. package/dist/components/onboarding/stages/OnboardingStageKeySave.svelte.d.ts +0 -8
  153. package/dist/components/onboarding/stages/OnboardingStageWelcome.svelte +0 -56
  154. package/dist/components/onboarding/stages/OnboardingStageWelcome.svelte.d.ts +0 -9
  155. package/dist/components/util/TimedButton.svelte +0 -37
  156. package/dist/components/util/TimedButton.svelte.d.ts +0 -7
@@ -2,6 +2,8 @@ import type { LocalRepositoryDto } from "../../../fetch-client";
2
2
  type Props = {
3
3
  repository: LocalRepositoryDto;
4
4
  immich?: boolean;
5
+ limit?: number;
6
+ onViewAll?: () => void;
5
7
  };
6
8
  declare const RepositorySnapshotsList: import("svelte").Component<Props, {}, "">;
7
9
  type RepositorySnapshotsList = ReturnType<typeof RepositorySnapshotsList>;
@@ -3,7 +3,9 @@
3
3
  import RelativeTime from "../../util/RelativeTime.svelte";
4
4
  import type { SnapshotDto } from "../../../fetch-client";
5
5
  import { getSnapshotActions } from "../../../services/snapshot.service";
6
- import { FormatBytes, HStack, Text } from "@immich/ui";
6
+ import { FormatBytes, Icon } from "@immich/ui";
7
+ import { mdiHistory } from "@mdi/js";
8
+ import { DateTime } from "luxon";
7
9
 
8
10
  type Props = {
9
11
  repositoryId: string;
@@ -15,29 +17,29 @@
15
17
  const { Restore, Rollback, Delete } = $derived(
16
18
  getSnapshotActions(repositoryId, snapshot, immich),
17
19
  );
20
+
21
+ const title = $derived(
22
+ DateTime.fromISO(snapshot.time).toLocaleString(DateTime.DATE_FULL),
23
+ );
18
24
  </script>
19
25
 
20
- <StackListItem actions={[Restore, Rollback, Delete]}>
21
- <HStack>
22
- <RelativeTime time={snapshot.time} />
26
+ <StackListItem {title} actions={[Restore, Rollback, Delete]}>
27
+ {#snippet icon()}
28
+ <Icon icon={mdiHistory} />
29
+ {/snippet}
23
30
 
24
- {#if snapshot.summary}
25
- <Text color="secondary">
26
- &middot; {snapshot.summary.totalFiles.toLocaleString()}
27
- {snapshot.summary.totalFiles > 1 ? "files" : "file"} &middot;
28
- <FormatBytes bytes={snapshot.summary.totalBytes} />
29
- </Text>
31
+ <RelativeTime time={snapshot.time} />
30
32
 
31
- {#if snapshot.summary.filesNew > 0 || snapshot.summary.filesChanged > 0}
32
- <Text color="muted" size="tiny">
33
- {#if snapshot.summary.filesNew > 0}
34
- &middot; {snapshot.summary.filesNew.toLocaleString()} new
35
- {/if}
36
- {#if snapshot.summary.filesChanged > 0}
37
- &middot; {snapshot.summary.filesChanged.toLocaleString()} changed
38
- {/if}
39
- </Text>
40
- {/if}
33
+ {#if snapshot.summary}
34
+ &middot; {snapshot.summary.totalFiles.toLocaleString()}
35
+ {snapshot.summary.totalFiles > 1 ? "files" : "file"} &middot;
36
+ <FormatBytes bytes={snapshot.summary.totalBytes} />
37
+
38
+ {#if snapshot.summary.filesNew > 0}
39
+ &middot; {snapshot.summary.filesNew.toLocaleString()} new
40
+ {/if}
41
+ {#if snapshot.summary.filesChanged > 0}
42
+ &middot; {snapshot.summary.filesChanged.toLocaleString()} changed
41
43
  {/if}
42
- </HStack>
44
+ {/if}
43
45
  </StackListItem>
@@ -0,0 +1,115 @@
1
+ <script lang="ts">
2
+ import ListToolbar from "../../ui/ListToolbar.svelte";
3
+ import Pagination from "../../ui/Pagination.svelte";
4
+ import StackList from "../../ui/StackList.svelte";
5
+ import StackListPlaceholder from "../../ui/StackListPlaceholder.svelte";
6
+ import OnEvents from "../../util/OnEvents.svelte";
7
+ import Suspense from "../../util/Suspense.svelte";
8
+ import type { LocalRepositoryDto, SnapshotDto } from "../../../fetch-client";
9
+ import {
10
+ useSnapshotEventHandler,
11
+ useSnapshots,
12
+ } from "../../../services/snapshot.service";
13
+ import { Stack, type ActionItem } from "@immich/ui";
14
+ import { mdiRadioboxBlank, mdiRadioboxMarked } from "@mdi/js";
15
+ import { DateTime } from "luxon";
16
+ import RepositorySnapshotsListItem from "./RepositorySnapshotsListItem.svelte";
17
+
18
+ type Props = {
19
+ repository: LocalRepositoryDto;
20
+ immich?: boolean;
21
+ pageSize?: number;
22
+ };
23
+
24
+ let { repository, immich = false, pageSize = 10 }: Props = $props();
25
+
26
+ // svelte-ignore state_referenced_locally
27
+ const query = useSnapshots(repository.id);
28
+ const { onRunUpdate } = useSnapshotEventHandler();
29
+
30
+ let search = $state("");
31
+ let newestFirst = $state(true);
32
+ let page = $state(1);
33
+
34
+ const onSearch = (value: string) => {
35
+ search = value;
36
+ page = 1;
37
+ };
38
+
39
+ const sortOptions = [
40
+ { newest: true, title: "Newest first" },
41
+ { newest: false, title: "Oldest first" },
42
+ ];
43
+
44
+ const sort = $derived({
45
+ label: newestFirst ? "Newest first" : "Oldest first",
46
+ items: sortOptions.map(
47
+ (option): ActionItem => ({
48
+ title: option.title,
49
+ icon: newestFirst === option.newest
50
+ ? mdiRadioboxMarked
51
+ : mdiRadioboxBlank,
52
+ onAction: () => (newestFirst = option.newest),
53
+ }),
54
+ ),
55
+ });
56
+
57
+ const matches = (snapshot: SnapshotDto) => {
58
+ const term = search.trim().toLowerCase();
59
+
60
+ if (!term) {
61
+ return true;
62
+ }
63
+
64
+ const date = DateTime.fromISO(snapshot.time).toLocaleString(
65
+ DateTime.DATE_FULL,
66
+ );
67
+
68
+ return [date, ...snapshot.paths, ...(snapshot.tags ?? [])]
69
+ .join(" ")
70
+ .toLowerCase()
71
+ .includes(term);
72
+ };
73
+
74
+ const filtered = $derived(
75
+ (query.data ?? [])
76
+ .filter(matches)
77
+ .toSorted((a, b) =>
78
+ newestFirst
79
+ ? +new Date(b.time) - +new Date(a.time)
80
+ : +new Date(a.time) - +new Date(b.time),
81
+ ),
82
+ );
83
+
84
+ const pageCount = $derived(
85
+ Math.max(Math.ceil(filtered.length / pageSize), 1),
86
+ );
87
+ const current = $derived(Math.min(page, pageCount));
88
+ const visible = $derived(
89
+ filtered.slice((current - 1) * pageSize, current * pageSize),
90
+ );
91
+ </script>
92
+
93
+ <OnEvents {onRunUpdate} />
94
+
95
+ <Stack gap={4}>
96
+ <ListToolbar placeholder="Search snapshots" {search} {onSearch} {sort} />
97
+
98
+ <StackList>
99
+ <Suspense {query}>
100
+ {#each visible as snapshot (snapshot.id)}
101
+ <RepositorySnapshotsListItem
102
+ repositoryId={repository.id}
103
+ {snapshot}
104
+ {immich}
105
+ />
106
+ {/each}
107
+
108
+ {#if filtered.length === 0}
109
+ <StackListPlaceholder>No snapshots found</StackListPlaceholder>
110
+ {/if}
111
+ </Suspense>
112
+ </StackList>
113
+
114
+ <Pagination page={current} {pageCount} onChange={(next) => (page = next)} />
115
+ </Stack>
@@ -0,0 +1,9 @@
1
+ import type { LocalRepositoryDto } from "../../../fetch-client";
2
+ type Props = {
3
+ repository: LocalRepositoryDto;
4
+ immich?: boolean;
5
+ pageSize?: number;
6
+ };
7
+ declare const RepositorySnapshotsPage: import("svelte").Component<Props, {}, "">;
8
+ type RepositorySnapshotsPage = ReturnType<typeof RepositorySnapshotsPage>;
9
+ export default RepositorySnapshotsPage;
@@ -1,7 +1,7 @@
1
1
  <script lang="ts">
2
2
  import type { LocalRepositoryDto } from "../../../fetch-client";
3
3
  import { Modal, ModalBody } from "@immich/ui";
4
- import RepositorySnapshotsList from "./RepositorySnapshotsList.svelte";
4
+ import RepositorySnapshotsPage from "./RepositorySnapshotsPage.svelte";
5
5
 
6
6
  type Props = {
7
7
  repository: LocalRepositoryDto;
@@ -13,6 +13,6 @@
13
13
 
14
14
  <Modal title={`Snapshots for ${repository.name}`} size="giant" {onClose}>
15
15
  <ModalBody>
16
- <RepositorySnapshotsList {repository} />
16
+ <RepositorySnapshotsPage {repository} />
17
17
  </ModalBody>
18
18
  </Modal>
@@ -18,10 +18,10 @@
18
18
  type Props = {
19
19
  local?: boolean;
20
20
  initialData?: RepositoryListResponseDto;
21
- onNavigate?: (route: string) => void;
21
+ onViewBackups?: () => void;
22
22
  };
23
23
 
24
- const { local, initialData, onNavigate }: Props = $props();
24
+ const { local, initialData, onViewBackups }: Props = $props();
25
25
 
26
26
  // svelte-ignore state_referenced_locally
27
27
  const query = useRepositories(initialData?.repositories);
@@ -36,12 +36,19 @@
36
36
  {:else if query.isError}
37
37
  <Alert color="danger">{getReadableErrorMessage(query.error)}</Alert>
38
38
  {:else if query.isSuccess}
39
- <Stack>
40
- <Stack direction="row">
41
- <DashboardBackupHealth repositories={query.data} {local} {onNavigate} />
42
- <DashboardInstall />
39
+ <Stack gap={6}>
40
+ <Stack direction="row" gap={4}>
41
+ <DashboardBackupHealth
42
+ repositories={query.data}
43
+ {local}
44
+ {onViewBackups}
45
+ />
46
+
47
+ {#if !local}
48
+ <DashboardInstall />
49
+ {/if}
43
50
  </Stack>
44
- <Stack direction="row">
51
+ <Stack direction="row" gap={4}>
45
52
  <DashboardAvgBackupTime repositories={query.data} />
46
53
  <DashboardDailyBackupTime repositories={query.data} />
47
54
  <DashboardTotalStored repositories={query.data} />
@@ -2,7 +2,7 @@ import type { RepositoryListResponseDto } from "../../fetch-client";
2
2
  type Props = {
3
3
  local?: boolean;
4
4
  initialData?: RepositoryListResponseDto;
5
- onNavigate?: (route: string) => void;
5
+ onViewBackups?: () => void;
6
6
  };
7
7
  declare const Dashboard: import("svelte").Component<Props, {}, "">;
8
8
  type Dashboard = ReturnType<typeof Dashboard>;
@@ -24,7 +24,7 @@
24
24
  );
25
25
  </script>
26
26
 
27
- <Card>
27
+ <Card class="border-primary-100 shadow-none">
28
28
  <CardBody>
29
29
  <VisualisationGauge
30
30
  title="Avg. Backup Time"
@@ -13,10 +13,10 @@
13
13
  type Props = {
14
14
  repositories: LocalRepositoryDto[];
15
15
  local?: boolean;
16
- onNavigate?: (route: string) => void;
16
+ onViewBackups?: () => void;
17
17
  };
18
18
 
19
- const { repositories, local, onNavigate }: Props = $props();
19
+ const { repositories, local, onViewBackups }: Props = $props();
20
20
 
21
21
  const total = $derived(repositories.length);
22
22
 
@@ -41,16 +41,12 @@
41
41
  );
42
42
  </script>
43
43
 
44
- <Card>
44
+ <Card class="border-primary-100 shadow-none">
45
45
  <CardHeader>
46
46
  <HStack class="justify-between">
47
47
  <CardTitle>Your Backups</CardTitle>
48
- {#if onNavigate}
49
- <Button
50
- variant="outline"
51
- size="tiny"
52
- onclick={() => onNavigate("backups")}
53
- >
48
+ {#if onViewBackups}
49
+ <Button variant="outline" size="tiny" onclick={onViewBackups}>
54
50
  View all
55
51
  </Button>
56
52
  {/if}
@@ -2,7 +2,7 @@ import type { LocalRepositoryDto } from "../../fetch-client";
2
2
  type Props = {
3
3
  repositories: LocalRepositoryDto[];
4
4
  local?: boolean;
5
- onNavigate?: (route: string) => void;
5
+ onViewBackups?: () => void;
6
6
  };
7
7
  declare const DashboardBackupHealth: import("svelte").Component<Props, {}, "">;
8
8
  type DashboardBackupHealth = ReturnType<typeof DashboardBackupHealth>;
@@ -3,7 +3,7 @@
3
3
  import VisualisationGauge from "../ui/VisualisationGauge.svelte";
4
4
  </script>
5
5
 
6
- <Card>
6
+ <Card class="border-primary-100 shadow-none">
7
7
  <CardBody>
8
8
  <VisualisationGauge title="Current Usage" content="$-/mo" />
9
9
  </CardBody>
@@ -21,7 +21,7 @@
21
21
  );
22
22
  </script>
23
23
 
24
- <Card>
24
+ <Card class="border-primary-100 shadow-none">
25
25
  <CardBody>
26
26
  <VisualisationGauge
27
27
  title="Daily Backup Time"
@@ -2,7 +2,7 @@
2
2
  import { Button, Card, CardBody, CardHeader, CardTitle, Logo } from "@immich/ui";
3
3
  </script>
4
4
 
5
- <Card>
5
+ <Card class="border-primary-100 shadow-none">
6
6
  <CardHeader>
7
7
  <CardTitle>Install FUTO Backups</CardTitle>
8
8
  </CardHeader>
@@ -1,25 +1,16 @@
1
1
  <script lang="ts">
2
2
  import type { LocalRepositoryDto } from "../../fetch-client";
3
+ import { Icon, modalManager, type ActionItem } from "@immich/ui";
3
4
  import {
4
- Card,
5
- CardBody,
6
- CardHeader,
7
- CardTitle,
8
- ContextMenuButton,
9
- HStack,
10
- Icon,
11
- modalManager,
12
- Text,
13
- type ActionItem,
14
- } from "@immich/ui";
15
- import {
16
- mdiAlertCircleOutline,
17
- mdiCheckCircleOutline,
18
- mdiDotsVertical,
5
+ mdiCloudCheckOutline,
6
+ mdiCloudOffOutline,
19
7
  mdiHistory,
20
8
  } from "@mdi/js";
21
- import RelativeTime from "../util/RelativeTime.svelte";
22
9
  import MetricsHistoryModal from "../backups/metrics-history/MetricsHistoryModal.svelte";
10
+ import StackList from "../ui/StackList.svelte";
11
+ import StackListPlaceholder from "../ui/StackListPlaceholder.svelte";
12
+ import StackListItem from "../ui/StackListItem.svelte";
13
+ import RelativeTime from "../util/RelativeTime.svelte";
23
14
 
24
15
  type Props = {
25
16
  repositories: LocalRepositoryDto[];
@@ -38,67 +29,45 @@
38
29
  .slice(0, 5),
39
30
  );
40
31
 
41
- const getActions = (repository: LocalRepositoryDto): ActionItem[] => [
42
- {
43
- title: "View history",
44
- icon: mdiHistory,
45
- onAction: () =>
46
- void modalManager.open(MetricsHistoryModal, { repository }),
47
- },
48
- ];
32
+ const getActions = (repository: LocalRepositoryDto): ActionItem[] =>
33
+ local
34
+ ? []
35
+ : [
36
+ {
37
+ title: "View history",
38
+ icon: mdiHistory,
39
+ onAction: () =>
40
+ void modalManager.open(MetricsHistoryModal, { repository }),
41
+ },
42
+ ];
49
43
  </script>
50
44
 
51
- <Card>
52
- <CardHeader>
53
- <CardTitle>Recent Backups</CardTitle>
54
- </CardHeader>
55
- <CardBody>
56
- {#if recentAttempts.length === 0}
57
- <Text color="secondary">
58
- Completed backups will appear here once your first backup runs.
59
- </Text>
60
- {:else}
61
- <div>
62
- {#each recentAttempts as repo, index (repo.id)}
63
- {#if index > 0}
64
- <hr
65
- style="border: none; border-top: 1px solid var(--immich-ui-default-border);"
66
- />
67
- {/if}
68
- <HStack class="justify-between py-2">
69
- <HStack class="gap-2">
70
- {#if repo.metrics.lastBackup === repo.metrics.lastSuccessfulBackup}
71
- <Icon
72
- icon={mdiCheckCircleOutline}
73
- size="16"
74
- class="text-success-500"
75
- />
76
- {:else}
77
- <Icon
78
- icon={mdiAlertCircleOutline}
79
- size="16"
80
- class="text-danger-500"
81
- />
82
- {/if}
83
- <Text>{repo.name}</Text>
84
- </HStack>
85
- <HStack class="gap-1">
86
- <Text color="secondary" size="small">
87
- <RelativeTime time={repo.metrics.lastBackup!} />
88
- </Text>
89
- {#if !local}
90
- <ContextMenuButton
91
- icon={mdiDotsVertical}
92
- aria-label="Options"
93
- items={getActions(repo)}
94
- variant="ghost"
95
- color="secondary"
96
- />
97
- {/if}
98
- </HStack>
99
- </HStack>
100
- {/each}
101
- </div>
102
- {/if}
103
- </CardBody>
104
- </Card>
45
+ <StackList>
46
+ {#snippet title()}
47
+ Recent backups
48
+ {/snippet}
49
+
50
+ {#if recentAttempts.length === 0}
51
+ <StackListPlaceholder>
52
+ Completed backups will appear here once your first backup runs.
53
+ </StackListPlaceholder>
54
+ {/if}
55
+
56
+ {#each recentAttempts as repository (repository.id)}
57
+ {@const successful =
58
+ repository.metrics.lastBackup === repository.metrics.lastSuccessfulBackup}
59
+
60
+ <StackListItem
61
+ title={repository.name}
62
+ color={successful ? "success" : "danger"}
63
+ actions={getActions(repository)}
64
+ >
65
+ {#snippet icon()}
66
+ <Icon icon={successful ? mdiCloudCheckOutline : mdiCloudOffOutline} />
67
+ {/snippet}
68
+
69
+ {successful ? "Backed up" : "Attempted"}
70
+ <RelativeTime time={repository.metrics.lastBackup!} />
71
+ </StackListItem>
72
+ {/each}
73
+ </StackList>
@@ -18,7 +18,7 @@
18
18
  );
19
19
  </script>
20
20
 
21
- <Card>
21
+ <Card class="border-primary-100 shadow-none">
22
22
  <CardBody>
23
23
  <VisualisationGauge
24
24
  title="Total Stored"
@@ -0,0 +1,12 @@
1
+ <script lang="ts">
2
+ import { Container } from "@immich/ui";
3
+ import ImmichSettingsBackupContents from "./settings/ImmichSettingsBackupContents.svelte";
4
+ import ImmichSettingsSchedule from "./settings/ImmichSettingsSchedule.svelte";
5
+ import ImmichSettingsStorage from "./settings/ImmichSettingsStorage.svelte";
6
+ </script>
7
+
8
+ <Container size="large" center>
9
+ <ImmichSettingsBackupContents />
10
+ <ImmichSettingsSchedule />
11
+ <ImmichSettingsStorage />
12
+ </Container>
@@ -0,0 +1,18 @@
1
+ interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
2
+ new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
3
+ $$bindings?: Bindings;
4
+ } & Exports;
5
+ (internal: unknown, props: {
6
+ $$events?: Events;
7
+ $$slots?: Slots;
8
+ }): Exports & {
9
+ $set?: any;
10
+ $on?: any;
11
+ };
12
+ z_$$bindings?: Bindings;
13
+ }
14
+ declare const ImmichBackupSettings: $$__sveltets_2_IsomorphicComponent<Record<string, never>, {
15
+ [evt: string]: CustomEvent<any>;
16
+ }, {}, {}, string>;
17
+ type ImmichBackupSettings = InstanceType<typeof ImmichBackupSettings>;
18
+ export default ImmichBackupSettings;
@@ -0,0 +1,73 @@
1
+ <script lang="ts">
2
+ import RelativeTime from "../../util/RelativeTime.svelte";
3
+ import { FormatBytes, Icon, Stack, Text } from "@immich/ui";
4
+ import { mdiChevronRight, mdiCloudAlertOutline, mdiCloudUpload } from "@mdi/js";
5
+
6
+ type Props = {
7
+ configured: boolean;
8
+ lastBackup?: string;
9
+ failed?: boolean;
10
+ sizeBytes?: number;
11
+ href?: string;
12
+ onclick?: () => void;
13
+ class?: string;
14
+ };
15
+
16
+ const {
17
+ configured,
18
+ lastBackup,
19
+ failed = false,
20
+ sizeBytes,
21
+ href,
22
+ onclick,
23
+ class: className,
24
+ }: Props = $props();
25
+
26
+ const shell =
27
+ "border-primary/25 hover:bg-primary/5 flex w-full items-center gap-3 rounded-xl border px-3 py-2.5 text-start transition-colors";
28
+ </script>
29
+
30
+ {#snippet body()}
31
+ <Icon
32
+ icon={failed ? mdiCloudAlertOutline : mdiCloudUpload}
33
+ size="2em"
34
+ class="{failed ? 'text-danger' : 'text-primary'} shrink-0"
35
+ />
36
+
37
+ {#if !configured}
38
+ <Text color="primary" fontWeight="medium" class="flex-1 whitespace-nowrap">
39
+ Set up Backups
40
+ </Text>
41
+ {:else}
42
+ <Stack gap={0} class="min-w-0 flex-1">
43
+ <Text fontWeight="medium" color={failed ? "danger" : "primary"}>
44
+ {failed ? "Backup failed" : "Last Backup"}
45
+ </Text>
46
+
47
+ <Text size="small" color="muted" class="truncate">
48
+ {#if lastBackup}
49
+ <RelativeTime time={lastBackup} />
50
+ {#if sizeBytes !== undefined}
51
+ &nbsp;&middot; <FormatBytes bytes={sizeBytes} />
52
+ {/if}
53
+ {:else}
54
+ No backups yet
55
+ {/if}
56
+ </Text>
57
+ </Stack>
58
+
59
+ <Icon icon={mdiChevronRight} size="1.4em" class="text-muted shrink-0" />
60
+ {/if}
61
+ {/snippet}
62
+
63
+ <div class={className ?? ""}>
64
+ {#if href}
65
+ <a {href} {onclick} class={shell}>
66
+ {@render body()}
67
+ </a>
68
+ {:else}
69
+ <button type="button" {onclick} class={shell}>
70
+ {@render body()}
71
+ </button>
72
+ {/if}
73
+ </div>
@@ -0,0 +1,12 @@
1
+ type Props = {
2
+ configured: boolean;
3
+ lastBackup?: string;
4
+ failed?: boolean;
5
+ sizeBytes?: number;
6
+ href?: string;
7
+ onclick?: () => void;
8
+ class?: string;
9
+ };
10
+ declare const ImmichBackupsCard: import("svelte").Component<Props, {}, "">;
11
+ type ImmichBackupsCard = ReturnType<typeof ImmichBackupsCard>;
12
+ export default ImmichBackupsCard;