@futo-org/backups-orchestrator-ui 0.27.0 → 0.30.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 (163) 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 +23 -12
  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 +136 -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 +109 -0
  38. package/dist/components/integrations/immich/ImmichBackupsCard.svelte.d.ts +13 -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 +16 -3
  42. package/dist/components/integrations/immich/ImmichBackupsPage.svelte.d.ts +8 -1
  43. package/dist/components/integrations/immich/ImmichBackupsSidebarItem.svelte +102 -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 +32 -0
  73. package/dist/components/onboarding/upsell/UpsellFrequentlyAskedQuestions.svelte.d.ts +11 -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 +40 -0
  81. package/dist/components/onboarding/upsell/UpsellPage.svelte.d.ts +14 -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 +143 -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/MockImmichHideBackupsReminder.svelte +12 -0
  99. package/dist/components/test/immich/MockImmichHideBackupsReminder.svelte.d.ts +3 -0
  100. package/dist/components/test/immich/MockImmichPhotos.svelte +36 -0
  101. package/dist/components/test/immich/MockImmichPhotos.svelte.d.ts +18 -0
  102. package/dist/components/test/immich/MockImmichPurchaseInfo.svelte +29 -0
  103. package/dist/components/test/immich/MockImmichPurchaseInfo.svelte.d.ts +18 -0
  104. package/dist/components/test/immich/MockImmichServerStatus.svelte +9 -0
  105. package/dist/components/test/immich/MockImmichServerStatus.svelte.d.ts +18 -0
  106. package/dist/components/test/immich/MockImmichSidebar.svelte +20 -0
  107. package/dist/components/test/immich/MockImmichSidebar.svelte.d.ts +7 -0
  108. package/dist/components/test/immich/MockImmichStorageSpace.svelte +25 -0
  109. package/dist/components/test/immich/MockImmichStorageSpace.svelte.d.ts +7 -0
  110. package/dist/components/ui/Accordion.svelte +67 -0
  111. package/dist/components/ui/Accordion.svelte.d.ts +11 -0
  112. package/dist/components/ui/ListToolbar.svelte +60 -0
  113. package/dist/components/ui/ListToolbar.svelte.d.ts +16 -0
  114. package/dist/components/ui/PageLayout.svelte +21 -9
  115. package/dist/components/ui/PageLayout.svelte.d.ts +1 -0
  116. package/dist/components/ui/Pagination.svelte +80 -0
  117. package/dist/components/ui/Pagination.svelte.d.ts +8 -0
  118. package/dist/components/ui/PathListField.svelte +17 -14
  119. package/dist/components/ui/StackList.svelte +18 -18
  120. package/dist/components/ui/StackList.svelte.d.ts +6 -27
  121. package/dist/components/ui/StackListItem.svelte +47 -25
  122. package/dist/components/ui/StackListItem.svelte.d.ts +5 -2
  123. package/dist/components/ui/StackListOption.svelte +33 -0
  124. package/dist/components/ui/StackListOption.svelte.d.ts +11 -0
  125. package/dist/components/ui/StackListPlaceholder.svelte +12 -0
  126. package/dist/components/ui/StackListPlaceholder.svelte.d.ts +7 -0
  127. package/dist/components/ui/StepNumber.svelte +20 -0
  128. package/dist/components/ui/StepNumber.svelte.d.ts +7 -0
  129. package/dist/components/util/Suspense.svelte +7 -3
  130. package/dist/components/util/YuccaContext.svelte +1 -1
  131. package/dist/events.js +1 -1
  132. package/dist/fetch-client.d.ts +2 -0
  133. package/dist/index.d.ts +6 -1
  134. package/dist/index.js +6 -1
  135. package/dist/services/backend.service.js +3 -0
  136. package/dist/services/immich.integration.service.d.ts +1 -1
  137. package/dist/services/immich.integration.service.js +2 -3
  138. package/dist/services/integrations.service.d.ts +1 -0
  139. package/dist/services/integrations.service.js +2 -1
  140. package/dist/services/log.service.svelte.d.ts +12 -0
  141. package/dist/services/log.service.svelte.js +15 -1
  142. package/dist/services/repository.service.js +9 -3
  143. package/dist/services/runHistory.service.d.ts +1 -1
  144. package/dist/services/runHistory.service.js +5 -4
  145. package/dist/utils/format.d.ts +1 -1
  146. package/dist/utils/format.js +2 -2
  147. package/package.json +3 -3
  148. package/dist/components/backups/dialogs/ViewLogModal.svelte +0 -208
  149. package/dist/components/backups/dialogs/ViewLogModal.svelte.d.ts +0 -7
  150. package/dist/components/integrations/immich/ImmichConfigureBackup.svelte +0 -403
  151. package/dist/components/integrations/immich/ImmichConfigureBackup.svelte.d.ts +0 -9
  152. package/dist/components/integrations/immich/ImmichConfirmDefaultBackup.svelte +0 -80
  153. package/dist/components/integrations/immich/ImmichConfirmDefaultBackup.svelte.d.ts +0 -8
  154. package/dist/components/onboarding/stages/OnboardingStageKeyConfirm.svelte +0 -56
  155. package/dist/components/onboarding/stages/OnboardingStageKeyConfirm.svelte.d.ts +0 -9
  156. package/dist/components/onboarding/stages/OnboardingStageKeyIntro.svelte +0 -50
  157. package/dist/components/onboarding/stages/OnboardingStageKeyIntro.svelte.d.ts +0 -7
  158. package/dist/components/onboarding/stages/OnboardingStageKeySave.svelte +0 -44
  159. package/dist/components/onboarding/stages/OnboardingStageKeySave.svelte.d.ts +0 -8
  160. package/dist/components/onboarding/stages/OnboardingStageWelcome.svelte +0 -56
  161. package/dist/components/onboarding/stages/OnboardingStageWelcome.svelte.d.ts +0 -9
  162. package/dist/components/util/TimedButton.svelte +0 -37
  163. package/dist/components/util/TimedButton.svelte.d.ts +0 -7
@@ -0,0 +1,9 @@
1
+ type Props = {
2
+ price: string;
3
+ onGetStarted: () => void;
4
+ onLearnMore: () => void;
5
+ onClose: () => void;
6
+ };
7
+ declare const UpsellModal: import("svelte").Component<Props, {}, "">;
8
+ type UpsellModal = ReturnType<typeof UpsellModal>;
9
+ export default UpsellModal;
@@ -0,0 +1,40 @@
1
+ <script lang="ts">
2
+ import { Card, CardBody, Container, HStack, Stack } from "@immich/ui";
3
+ import type { Snippet } from "svelte";
4
+ import UpsellBackupFeaturesGrid from "./UpsellBackupFeaturesGrid.svelte";
5
+ import UpsellExplainFutoBackups from "./UpsellExplainFutoBackups.svelte";
6
+ import UpsellFrequentlyAskedQuestions from "./UpsellFrequentlyAskedQuestions.svelte";
7
+ import UpsellHowItWorks from "./UpsellHowItWorks.svelte";
8
+
9
+ type Question = {
10
+ title: string;
11
+ answer: string | Snippet;
12
+ };
13
+
14
+ type Props = {
15
+ price: string;
16
+ includedStorage: string;
17
+ questions: Question[];
18
+ onGetStarted: () => void;
19
+ };
20
+
21
+ const { price, includedStorage, questions, onGetStarted }: Props = $props();
22
+ </script>
23
+
24
+ <Container size="large" center>
25
+ <Stack gap={8} class="py-4">
26
+ <Card color="primary" class="shadow-none">
27
+ <CardBody class="p-8">
28
+ <HStack gap={8} class="justify-between">
29
+ <UpsellExplainFutoBackups {price} {includedStorage} {onGetStarted} />
30
+
31
+ <UpsellHowItWorks color="secondary" />
32
+ </HStack>
33
+ </CardBody>
34
+ </Card>
35
+
36
+ <UpsellBackupFeaturesGrid />
37
+
38
+ <UpsellFrequentlyAskedQuestions {questions} />
39
+ </Stack>
40
+ </Container>
@@ -0,0 +1,14 @@
1
+ import type { Snippet } from "svelte";
2
+ type Question = {
3
+ title: string;
4
+ answer: string | Snippet;
5
+ };
6
+ type Props = {
7
+ price: string;
8
+ includedStorage: string;
9
+ questions: Question[];
10
+ onGetStarted: () => void;
11
+ };
12
+ declare const UpsellPage: import("svelte").Component<Props, {}, "">;
13
+ type UpsellPage = ReturnType<typeof UpsellPage>;
14
+ export default UpsellPage;
@@ -0,0 +1,58 @@
1
+ <script lang="ts">
2
+ import { Button, Heading, HStack, Icon, Stack, Text } from "@immich/ui";
3
+ import { mdiCheckCircle } from "@mdi/js";
4
+ import UpsellFutoBackupsBadge from "./UpsellFutoBackupsBadge.svelte";
5
+
6
+ type Props = {
7
+ price: string;
8
+ onGetStarted: () => void;
9
+ onLearnMore: () => void;
10
+ };
11
+
12
+ const { price, onGetStarted, onLearnMore }: Props = $props();
13
+
14
+ const benefits = [
15
+ "Cloud copy while your originals stay local",
16
+ "Encrypted backups protect your data",
17
+ "Restore if your local storage fails",
18
+ ];
19
+ </script>
20
+
21
+ <Stack gap={6}>
22
+ <UpsellFutoBackupsBadge />
23
+
24
+ <Stack gap={4}>
25
+ <Heading size="medium" color="primary" fontWeight="bold"
26
+ >Protect your Immich library</Heading
27
+ >
28
+
29
+ <Text>
30
+ FUTO Backups adds hosted cloud backup storage to your existing Immich
31
+ setup, so you have another copy if something happens locally.
32
+ </Text>
33
+ </Stack>
34
+
35
+ <Stack gap={2}>
36
+ {#each benefits as benefit (benefit)}
37
+ <HStack gap={3} class="items-center">
38
+ <Icon
39
+ icon={mdiCheckCircle}
40
+ size="1.4em"
41
+ class="text-primary shrink-0"
42
+ />
43
+ <Text>{benefit}</Text>
44
+ </HStack>
45
+ {/each}
46
+ </Stack>
47
+
48
+ <Text fontWeight="semi-bold">
49
+ Starting at {price}/month. Additional storage billed by usage.
50
+ </Text>
51
+
52
+ <HStack gap={6} class="items-center">
53
+ <Button shape="round" onclick={onGetStarted}>Get Started</Button>
54
+ <Button variant="ghost" shape="round" onclick={onLearnMore}>
55
+ Learn More
56
+ </Button>
57
+ </HStack>
58
+ </Stack>
@@ -0,0 +1,8 @@
1
+ type Props = {
2
+ price: string;
3
+ onGetStarted: () => void;
4
+ onLearnMore: () => void;
5
+ };
6
+ declare const UpsellProtectYourImmichLibrary: import("svelte").Component<Props, {}, "">;
7
+ type UpsellProtectYourImmichLibrary = ReturnType<typeof UpsellProtectYourImmichLibrary>;
8
+ export default UpsellProtectYourImmichLibrary;
@@ -1,8 +1,9 @@
1
1
  <script lang="ts">
2
2
  import { useRepositories } from "../../services/repository.service";
3
- import { HStack, IconButton, Stack, Text } from "@immich/ui";
3
+ import { IconButton, Stack, Text } from "@immich/ui";
4
4
  import { mdiArrowDown, mdiArrowUp, mdiClose, mdiPlus } from "@mdi/js";
5
5
  import StackList from "../ui/StackList.svelte";
6
+ import StackListItem from "../ui/StackListItem.svelte";
6
7
 
7
8
  type Props = {
8
9
  repositories: string[];
@@ -46,41 +47,44 @@
46
47
  {#snippet title()}Repositories{/snippet}
47
48
 
48
49
  {#each repositories as id, index (id)}
49
- <HStack gap={2} class="px-4 py-3">
50
+ <StackListItem>
50
51
  <Text class="grow truncate" size="small">{nameById[id] ?? id}</Text>
51
- <IconButton
52
- icon={mdiArrowUp}
53
- size="tiny"
54
- variant="ghost"
55
- aria-label="Move up"
56
- disabled={index === 0}
57
- onclick={() => move(index, -1)}
58
- />
59
- <IconButton
60
- icon={mdiArrowDown}
61
- size="tiny"
62
- variant="ghost"
63
- aria-label="Move down"
64
- disabled={index === repositories.length - 1}
65
- onclick={() => move(index, 1)}
66
- />
67
- <IconButton
68
- icon={mdiClose}
69
- size="tiny"
70
- color="danger"
71
- variant="ghost"
72
- aria-label="Remove"
73
- onclick={() => remove(id)}
74
- />
75
- </HStack>
52
+
53
+ {#snippet trailing()}
54
+ <IconButton
55
+ icon={mdiArrowUp}
56
+ size="tiny"
57
+ variant="ghost"
58
+ aria-label="Move up"
59
+ disabled={index === 0}
60
+ onclick={() => move(index, -1)}
61
+ />
62
+ <IconButton
63
+ icon={mdiArrowDown}
64
+ size="tiny"
65
+ variant="ghost"
66
+ aria-label="Move down"
67
+ disabled={index === repositories.length - 1}
68
+ onclick={() => move(index, 1)}
69
+ />
70
+ <IconButton
71
+ icon={mdiClose}
72
+ size="tiny"
73
+ color="danger"
74
+ variant="ghost"
75
+ aria-label="Remove"
76
+ onclick={() => remove(id)}
77
+ />
78
+ {/snippet}
79
+ </StackListItem>
76
80
  {/each}
77
81
 
78
82
  {#if repositories.length === 0}
79
- <HStack class="px-4 py-3">
83
+ <StackListItem>
80
84
  <Text color="secondary" size="small">
81
85
  No repositories in this schedule yet.
82
86
  </Text>
83
- </HStack>
87
+ </StackListItem>
84
88
  {/if}
85
89
  </StackList>
86
90
 
@@ -89,16 +93,19 @@
89
93
  {#snippet title()}Available{/snippet}
90
94
 
91
95
  {#each available as repo (repo.id)}
92
- <HStack gap={2} class="px-4 py-3">
96
+ <StackListItem>
93
97
  <Text class="grow truncate" size="small">{repo.name}</Text>
94
- <IconButton
95
- icon={mdiPlus}
96
- size="tiny"
97
- variant="ghost"
98
- aria-label="Add"
99
- onclick={() => add(repo.id)}
100
- />
101
- </HStack>
98
+
99
+ {#snippet trailing()}
100
+ <IconButton
101
+ icon={mdiPlus}
102
+ size="tiny"
103
+ variant="ghost"
104
+ aria-label="Add"
105
+ onclick={() => add(repo.id)}
106
+ />
107
+ {/snippet}
108
+ </StackListItem>
102
109
  {/each}
103
110
  </StackList>
104
111
  {/if}
@@ -3,7 +3,8 @@
3
3
  import RelativeTime from "../util/RelativeTime.svelte";
4
4
  import type { ScheduleDto } from "../../fetch-client";
5
5
  import { getScheduleActions } from "../../services/schedule.service";
6
- import { Badge, HStack, Stack, Text } from "@immich/ui";
6
+ import { Badge, Icon } from "@immich/ui";
7
+ import { mdiClockOutline } from "@mdi/js";
7
8
 
8
9
  type Props = {
9
10
  schedule: ScheduleDto;
@@ -17,31 +18,36 @@
17
18
  );
18
19
  </script>
19
20
 
20
- <StackListItem actions={[Resume, Pause, Configure, Delete]}>
21
- <Stack gap={0} class="min-w-0">
22
- <HStack gap={1} class="items-baseline">
23
- <Text>{schedule.name}</Text>
24
- <Badge size="tiny" color="info">{schedule.cron}</Badge>
25
- {#if schedule.paused}
26
- <Badge size="tiny" color="warning">Paused</Badge>
27
- {/if}
28
- {#if schedule.lastRun}
29
- <Badge size="tiny" color="success">
30
- Ran <RelativeTime time={schedule.lastRun} />
31
- </Badge>
32
- {:else}
33
- <Badge size="tiny" color="secondary">Never run</Badge>
34
- {/if}
35
- </HStack>
36
-
37
- {#if schedule.repositories.length > 0}
38
- <Text size="small" color="secondary">
39
- {schedule.repositories
40
- .map((repositoryId) => repositoryNames[repositoryId] ?? repositoryId)
41
- .join(", ")}
42
- </Text>
21
+ <StackListItem
22
+ title={schedule.name}
23
+ color={schedule.paused ? "warning" : "primary"}
24
+ actions={[Resume, Pause, Configure, Delete]}
25
+ >
26
+ {#snippet icon()}
27
+ <Icon icon={mdiClockOutline} />
28
+ {/snippet}
29
+
30
+ {#if schedule.repositories.length > 0}
31
+ {schedule.repositories
32
+ .map((repositoryId) => repositoryNames[repositoryId] ?? repositoryId)
33
+ .join(", ")}
34
+ {:else}
35
+ No backups in this schedule.
36
+ {/if}
37
+
38
+ {#snippet trailing()}
39
+ <Badge size="tiny" color="info">{schedule.cron}</Badge>
40
+
41
+ {#if schedule.paused}
42
+ <Badge size="tiny" color="warning">Paused</Badge>
43
+ {/if}
44
+
45
+ {#if schedule.lastRun}
46
+ <Badge size="tiny" color="success">
47
+ Ran <RelativeTime time={schedule.lastRun} />
48
+ </Badge>
43
49
  {:else}
44
- <Text size="small" color="secondary">No backups in this schedule.</Text>
50
+ <Badge size="tiny" color="secondary">Never run</Badge>
45
51
  {/if}
46
- </Stack>
52
+ {/snippet}
47
53
  </StackListItem>
@@ -4,9 +4,11 @@
4
4
  useScheduleEventHandler,
5
5
  useSchedules,
6
6
  } from "../../services/schedule.service";
7
- import { Button, HStack, modalManager, Stack, Text } from "@immich/ui";
7
+ import { Button, HStack, modalManager, Stack } from "@immich/ui";
8
8
  import StackList from "../ui/StackList.svelte";
9
+ import StackListPlaceholder from "../ui/StackListPlaceholder.svelte";
9
10
  import OnEvents from "../util/OnEvents.svelte";
11
+ import Suspense from "../util/Suspense.svelte";
10
12
  import CreateScheduleModal from "./dialogs/CreateScheduleModal.svelte";
11
13
  import ScheduleItem from "./ScheduleItem.svelte";
12
14
 
@@ -31,16 +33,20 @@
31
33
  <OnEvents {onScheduleCreate} {onScheduleUpdate} {onScheduleDelete} />
32
34
 
33
35
  <Stack gap={2}>
34
- <StackList query={schedulesQuery}>
36
+ <StackList>
35
37
  {#snippet title()}Schedules{/snippet}
36
- {#snippet children(schedules)}
37
- {#each schedules as schedule (schedule.id)}
38
- <ScheduleItem {schedule} {repositoryNames} />
39
- {/each}
40
- {#if schedules.length === 0}
41
- <Text class="text-center py-6" color="muted">No schedules yet.</Text>
42
- {/if}
43
- {/snippet}
38
+
39
+ <Suspense query={schedulesQuery}>
40
+ {#snippet children(schedules)}
41
+ {#each schedules as schedule (schedule.id)}
42
+ <ScheduleItem {schedule} {repositoryNames} />
43
+ {/each}
44
+
45
+ {#if schedules.length === 0}
46
+ <StackListPlaceholder>No schedules yet.</StackListPlaceholder>
47
+ {/if}
48
+ {/snippet}
49
+ </Suspense>
44
50
  </StackList>
45
51
 
46
52
  <HStack>
@@ -0,0 +1,8 @@
1
+ <script lang="ts">
2
+ import { Container } from "@immich/ui";
3
+ import SettingsBackends from "./SettingsBackends.svelte";
4
+ </script>
5
+
6
+ <Container size="large" center>
7
+ <SettingsBackends />
8
+ </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 GlobalSettings: $$__sveltets_2_IsomorphicComponent<Record<string, never>, {
15
+ [evt: string]: CustomEvent<any>;
16
+ }, {}, {}, string>;
17
+ type GlobalSettings = InstanceType<typeof GlobalSettings>;
18
+ export default GlobalSettings;
@@ -0,0 +1,16 @@
1
+ <script lang="ts">
2
+ import BackendsList from "../backends/BackendsList.svelte";
3
+ import Accordion from "../ui/Accordion.svelte";
4
+ import { Stack } from "@immich/ui";
5
+ import { mdiCloudOutline } from "@mdi/js";
6
+ </script>
7
+
8
+ <Accordion
9
+ title="Storage backends"
10
+ subtitle="Manage the storage services your backups can be sent to."
11
+ icon={mdiCloudOutline}
12
+ >
13
+ <Stack gap={4} class="pt-2">
14
+ <BackendsList actions />
15
+ </Stack>
16
+ </Accordion>
@@ -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 SettingsBackends: $$__sveltets_2_IsomorphicComponent<Record<string, never>, {
15
+ [evt: string]: CustomEvent<any>;
16
+ }, {}, {}, string>;
17
+ type SettingsBackends = InstanceType<typeof SettingsBackends>;
18
+ export default SettingsBackends;
@@ -0,0 +1,69 @@
1
+ <script lang="ts">
2
+ import Accordion from "../ui/Accordion.svelte";
3
+ import {
4
+ Badge,
5
+ Button,
6
+ Field,
7
+ HStack,
8
+ Input,
9
+ Label,
10
+ Stack,
11
+ Text,
12
+ } from "@immich/ui";
13
+ import { mdiCreditCardOutline } from "@mdi/js";
14
+
15
+ type Props = {
16
+ onManageBilling: () => void;
17
+ };
18
+
19
+ const { onManageBilling }: Props = $props();
20
+ </script>
21
+
22
+ <Accordion
23
+ title="Billing"
24
+ subtitle="Manage your FUTO Backups subscription and billing."
25
+ icon={mdiCreditCardOutline}
26
+ >
27
+ <Stack gap={4} class="pt-2">
28
+ <Accordion
29
+ title="FUTO Backups"
30
+ subtitle="Hosted cloud backup storage for your Immich library."
31
+ isOpen
32
+ >
33
+ <Stack gap={4} class="pt-2">
34
+ <HStack gap={4} class="items-start justify-between">
35
+ <Stack gap={0}>
36
+ <Label label="Status" color="primary" />
37
+ <Text size="small" color="muted">
38
+ Connection state for your backup storage.
39
+ </Text>
40
+ </Stack>
41
+
42
+ <Badge color="primary">Active</Badge>
43
+ </HStack>
44
+
45
+ <Field
46
+ label="Storage Used"
47
+ description="How much storage your backups are using."
48
+ color="primary"
49
+ readOnly
50
+ >
51
+ <Input value="100 GiB" />
52
+ </Field>
53
+
54
+ <Field
55
+ label="Next Billing Date"
56
+ description="When your next FUTO Backups charge will occur."
57
+ color="primary"
58
+ readOnly
59
+ >
60
+ <Input value="August 20, 2026" />
61
+ </Field>
62
+ </Stack>
63
+ </Accordion>
64
+
65
+ <HStack class="justify-end">
66
+ <Button shape="round" onclick={onManageBilling}>Manage billing</Button>
67
+ </HStack>
68
+ </Stack>
69
+ </Accordion>
@@ -0,0 +1,6 @@
1
+ type Props = {
2
+ onManageBilling: () => void;
3
+ };
4
+ declare const SettingsBilling: import("svelte").Component<Props, {}, "">;
5
+ type SettingsBilling = ReturnType<typeof SettingsBilling>;
6
+ export default SettingsBilling;
@@ -0,0 +1,47 @@
1
+ <script lang="ts">
2
+ import Accordion from "../ui/Accordion.svelte";
3
+ import { Button, Field, HStack, Stack, Switch } from "@immich/ui";
4
+ import { mdiBellOutline } from "@mdi/js";
5
+
6
+ const alerts = $state([
7
+ {
8
+ label: "Backup failures",
9
+ description: "Notify me when a backup fails.",
10
+ checked: true,
11
+ },
12
+ {
13
+ label: "Storage issues",
14
+ description: "Notify me when backup storage is unavailable or running low.",
15
+ checked: true,
16
+ },
17
+ {
18
+ label: "Billing issues",
19
+ description: "Notify me when FUTO Backups billing needs attention.",
20
+ checked: true,
21
+ },
22
+ ]);
23
+
24
+ type Props = {
25
+ onSave: () => void;
26
+ };
27
+
28
+ const { onSave }: Props = $props();
29
+ </script>
30
+
31
+ <Accordion
32
+ title="Notifications"
33
+ subtitle="Manage backup alerts."
34
+ icon={mdiBellOutline}
35
+ >
36
+ <Stack gap={4} class="pt-2">
37
+ {#each alerts as alert (alert.label)}
38
+ <Field label={alert.label} description={alert.description}>
39
+ <Switch bind:checked={alert.checked} />
40
+ </Field>
41
+ {/each}
42
+
43
+ <HStack class="justify-end">
44
+ <Button shape="round" onclick={onSave}>Save</Button>
45
+ </HStack>
46
+ </Stack>
47
+ </Accordion>
@@ -0,0 +1,6 @@
1
+ type Props = {
2
+ onSave: () => void;
3
+ };
4
+ declare const SettingsNotifications: import("svelte").Component<Props, {}, "">;
5
+ type SettingsNotifications = ReturnType<typeof SettingsNotifications>;
6
+ export default SettingsNotifications;