@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
@@ -1,39 +1,56 @@
1
1
  <script lang="ts">
2
2
  import OnboardingBootstrapError from "../../onboarding/OnboardingBootstrapError.svelte";
3
- import OnboardingStageBackupServices from "../../onboarding/stages/OnboardingStageBackupServices.svelte";
4
- import OnboardingStageKeyConfirm from "../../onboarding/stages/OnboardingStageKeyConfirm.svelte";
5
3
  import OnboardingStageKeyImport from "../../onboarding/stages/OnboardingStageKeyImport.svelte";
6
- import OnboardingStageKeyIntro from "../../onboarding/stages/OnboardingStageKeyIntro.svelte";
7
- import OnboardingStageSaveKey from "../../onboarding/stages/OnboardingStageKeySave.svelte";
8
4
  import OnboardingStageTelemetry from "../../onboarding/stages/OnboardingStageTelemetry.svelte";
9
- import OnboardingStageWelcome from "../../onboarding/stages/OnboardingStageWelcome.svelte";
5
+ import OnboardingStepFinishSetup from "../../onboarding/steps/OnboardingStep1FinishSetup.svelte";
6
+ import OnboardingStepConnectAccount from "../../onboarding/steps/OnboardingStep2ConnectAccount.svelte";
7
+ import OnboardingStepSaveRecoveryKey from "../../onboarding/steps/OnboardingStep3SaveRecoveryKey.svelte";
8
+ import OnboardingStepFirstBackup from "../../onboarding/steps/OnboardingStep4FirstBackup.svelte";
10
9
  import type { OnboardingStatusResponseDto } from "../../../fetch-client";
10
+ import {
11
+ handleSetupLocalStorage,
12
+ handleStartYuccaLogin,
13
+ } from "../../../services/backend.service";
14
+ import {
15
+ IMMICH_DEFAULT_CRON,
16
+ useConfigureImmichDefaults,
17
+ } from "../../../services/integrations.service";
11
18
  import {
12
19
  handleConfirmRecoveryKey,
13
20
  handleCurrentRecoveryKey,
14
21
  handleOnboardingStatus,
15
22
  } from "../../../services/onboarding.service";
16
- import { LoadingSpinner } from "@immich/ui";
23
+ import { LoadingSpinner, Modal, ModalBody } from "@immich/ui";
24
+ import cronstrue from "cronstrue";
17
25
  import { onMount, type Snippet } from "svelte";
18
- import ImmichConfigureBackup from "./ImmichConfigureBackup.svelte";
19
- import ImmichConfirmDefaultBackup from "./ImmichConfirmDefaultBackup.svelte";
26
+
27
+ type Stage =
28
+ | "idle"
29
+ | "intro"
30
+ | "telemetry"
31
+ | "connect"
32
+ | "key-import"
33
+ | "key"
34
+ | "backup"
35
+ | "finished";
20
36
 
21
37
  type Props = {
22
- onExit: () => void;
38
+ fallback: Snippet<[() => void]>;
23
39
  children: Snippet;
24
40
  };
25
41
 
26
- const { onExit, children }: Props = $props();
42
+ const { fallback, children }: Props = $props();
27
43
 
28
44
  let code = $state("");
29
- let backendId = $state("");
45
+ let storageLocation = $state("FUTO Backups");
30
46
  let status: OnboardingStatusResponseDto | undefined = $state();
31
- let stage:
32
- | `welcome`
33
- | `telemetry`
34
- | `key-${"intro" | "save" | "confirm" | "import"}`
35
- | `backup-${"service" | "confirm" | "create"}`
36
- | `finished` = $state("welcome");
47
+ let stage: Stage = $state("idle");
48
+ let resume: Stage = $state("intro");
49
+ let confirming = $state(false);
50
+
51
+ const defaults = useConfigureImmichDefaults();
52
+
53
+ const schedule = cronstrue.toString(IMMICH_DEFAULT_CRON, { verbose: true });
37
54
 
38
55
  onMount(() => {
39
56
  handleOnboardingStatus().then(async (data) => {
@@ -44,12 +61,12 @@
44
61
  }
45
62
 
46
63
  if (data.hasOnboardedKey) {
47
- stage =
48
- data.hasTelemetry === "none"
49
- ? "telemetry"
50
- : data.hasBackup
51
- ? "finished"
52
- : "backup-service";
64
+ if (data.hasBackup) {
65
+ stage = "finished";
66
+ return;
67
+ }
68
+
69
+ resume = data.hasTelemetry === "none" ? "telemetry" : "connect";
53
70
  } else {
54
71
  const { recoveryKey } = await handleCurrentRecoveryKey();
55
72
  code = recoveryKey;
@@ -57,75 +74,99 @@
57
74
  });
58
75
  });
59
76
 
60
- const onConfirmKey = async () => {
61
- await handleConfirmRecoveryKey();
62
- stage = "backup-service";
77
+ const onStart = () => (stage = resume);
78
+ const onCancel = () => (stage = "idle");
79
+
80
+ const afterTelemetry = () =>
81
+ (stage = status?.hasOnboardedKey && status.hasBackup
82
+ ? "finished"
83
+ : "connect");
84
+
85
+ const onBackendReady = () => (stage = status?.hasOnboardedKey ? "backup" : "key");
86
+
87
+ const onConnect = () => {
88
+ storageLocation = "FUTO Backups";
89
+ handleStartYuccaLogin(onBackendReady);
90
+ };
91
+
92
+ const onLocalStorage = () => {
93
+ storageLocation = "Local Storage";
94
+ handleSetupLocalStorage(onBackendReady);
63
95
  };
64
96
 
65
- const onSelectBackend = (id: string) => {
66
- backendId = id;
67
- stage = "backup-confirm";
97
+ const onConfirmKey = async () => {
98
+ confirming = true;
99
+
100
+ try {
101
+ await handleConfirmRecoveryKey();
102
+ stage = "backup";
103
+ } finally {
104
+ confirming = false;
105
+ }
68
106
  };
107
+
108
+ const onStartBackup = () =>
109
+ defaults.mutate(undefined, { onSuccess: () => (stage = "finished") });
69
110
  </script>
70
111
 
71
112
  {#if status === undefined || status.status === "not-ready"}
72
- <LoadingSpinner />
73
- {:else if status.status === "error"}
74
- <OnboardingBootstrapError error={status.error} onQuit={onExit} />
113
+ <div class="flex h-full items-center justify-center p-8">
114
+ <LoadingSpinner />
115
+ </div>
75
116
  {:else if stage === "finished"}
76
117
  {@render children()}
77
- {:else if stage === "welcome"}
78
- <OnboardingStageWelcome
79
- onNext={() => (stage = status?.hasTelemetry === "none" ? "telemetry" : "key-intro")}
80
- onImportKey={() => (stage = "key-import")}
81
- onCancel={onExit}
82
- />
118
+ {:else}
119
+ {@render fallback(onStart)}
120
+ {/if}
121
+
122
+ {#if status?.status === "error" && stage !== "idle"}
123
+ <OnboardingBootstrapError error={status.error} onQuit={onCancel} />
124
+ {:else if stage === "intro"}
125
+ <Modal size="small" title="FUTO Backups" onClose={onCancel}>
126
+ <ModalBody>
127
+ <OnboardingStepFinishSetup
128
+ onContinue={() =>
129
+ (stage = status?.hasTelemetry === "none" ? "telemetry" : "connect")}
130
+ onImportKey={() => (stage = "key-import")}
131
+ />
132
+ </ModalBody>
133
+ </Modal>
83
134
  {:else if stage === "telemetry"}
84
- <OnboardingStageTelemetry
85
- onContinue={() =>
86
- (stage = status?.hasOnboardedKey
87
- ? status.hasBackup
88
- ? "finished"
89
- : "backup-service"
90
- : "key-intro")}
91
- onCancel={onExit}
92
- />
135
+ <OnboardingStageTelemetry onContinue={afterTelemetry} {onCancel} />
136
+ {:else if stage === "connect"}
137
+ <Modal size="small" title="FUTO Backups" onClose={onCancel}>
138
+ <ModalBody>
139
+ <OnboardingStepConnectAccount {onConnect} {onLocalStorage} />
140
+ </ModalBody>
141
+ </Modal>
93
142
  {:else if stage === "key-import"}
94
143
  <OnboardingStageKeyImport
95
- onStart={() => (stage = "welcome")}
96
- onImported={() => (stage = "key-confirm")}
97
- onCancel={onExit}
98
- />
99
- {:else if stage === "key-intro"}
100
- <OnboardingStageKeyIntro
101
- onNext={() => (stage = "key-save")}
102
- onCancel={onExit}
103
- />
104
- {:else if stage === "key-save"}
105
- <OnboardingStageSaveKey
106
- {code}
107
- onNext={() => (stage = "key-confirm")}
108
- onCancel={onExit}
109
- />
110
- {:else if stage === "key-confirm"}
111
- <OnboardingStageKeyConfirm
112
- {code}
113
- onBack={() => (stage = "key-save")}
114
- onCancel={onExit}
115
- {onConfirmKey}
116
- />
117
- {:else if stage === "backup-service"}
118
- <OnboardingStageBackupServices onNext={onSelectBackend} onCancel={onExit} />
119
- {:else if stage === "backup-confirm"}
120
- <ImmichConfirmDefaultBackup
121
- onCustomize={() => (stage = "backup-create")}
122
- onConfirm={() => (stage = "finished")}
123
- onCancel={onExit}
124
- />
125
- {:else if stage === "backup-create"}
126
- <ImmichConfigureBackup
127
- onFinish={() => (stage = "finished")}
128
- onCancel={onExit}
129
- {backendId}
144
+ onStart={() => (stage = "intro")}
145
+ onImported={(key) => {
146
+ code = key;
147
+ stage = "connect";
148
+ }}
149
+ {onCancel}
130
150
  />
151
+ {:else if stage === "key"}
152
+ <Modal size="small" title="FUTO Backups" onClose={onCancel}>
153
+ <ModalBody>
154
+ <OnboardingStepSaveRecoveryKey
155
+ {code}
156
+ onContinue={onConfirmKey}
157
+ loading={confirming}
158
+ />
159
+ </ModalBody>
160
+ </Modal>
161
+ {:else if stage === "backup"}
162
+ <Modal size="small" title="FUTO Backups" onClose={onCancel}>
163
+ <ModalBody>
164
+ <OnboardingStepFirstBackup
165
+ {schedule}
166
+ {storageLocation}
167
+ {onStartBackup}
168
+ loading={defaults.isPending}
169
+ />
170
+ </ModalBody>
171
+ </Modal>
131
172
  {/if}
@@ -1,6 +1,6 @@
1
1
  import { type Snippet } from "svelte";
2
2
  type Props = {
3
- onExit: () => void;
3
+ fallback: Snippet<[() => void]>;
4
4
  children: Snippet;
5
5
  };
6
6
  declare const ImmichOnboardingSetupFlow: import("svelte").Component<Props, {}, "">;
@@ -0,0 +1,313 @@
1
+ <script lang="ts">
2
+ import Accordion from "../../../ui/Accordion.svelte";
3
+ import OnEvents from "../../../util/OnEvents.svelte";
4
+ import {
5
+ useConfigureImmichIntegration,
6
+ useIntegrationEventHandler,
7
+ useIntegrations,
8
+ } from "../../../../services/integrations.service";
9
+ import {
10
+ useRepositories,
11
+ useRepositoryEventHandler,
12
+ } from "../../../../services/repository.service";
13
+ import {
14
+ useScheduleEventHandler,
15
+ useSchedules,
16
+ } from "../../../../services/schedule.service";
17
+ import {
18
+ Button,
19
+ Checkbox,
20
+ Field,
21
+ HStack,
22
+ LoadingSpinner,
23
+ Select,
24
+ Stack,
25
+ Switch,
26
+ Text,
27
+ } from "@immich/ui";
28
+ import { mdiImageMultiple } from "@mdi/js";
29
+ import { SvelteSet } from "svelte/reactivity";
30
+
31
+ type Group = {
32
+ key: string;
33
+ label: string;
34
+ description: string;
35
+ folders: string[];
36
+ required?: boolean;
37
+ warning?: string;
38
+ warningColor?: "danger" | "warning";
39
+ };
40
+
41
+ const groups: Group[] = [
42
+ {
43
+ key: "media",
44
+ label: "Photos and videos",
45
+ description: "Your media uploaded directly to Immich.",
46
+ folders: ["upload", "profile", "library"],
47
+ warning: "It is highly recommended you back this up!",
48
+ warningColor: "danger",
49
+ },
50
+ {
51
+ key: "database",
52
+ label: "Database and metadata",
53
+ description:
54
+ "Albums, people, tags, favorites and other library details. Required to restore.",
55
+ folders: ["backups"],
56
+ required: true,
57
+ },
58
+ {
59
+ key: "thumbs",
60
+ label: "Thumbnails and previews",
61
+ description: "Generated photo previews, can be recreated later.",
62
+ folders: ["thumbs"],
63
+ warning:
64
+ "When restoring, you will see broken previews. Use the admin panel to regenerate them.",
65
+ warningColor: "warning",
66
+ },
67
+ {
68
+ key: "encoded",
69
+ label: "Encoded videos",
70
+ description: "Generated video previews, can be recreated later.",
71
+ folders: ["encoded-video"],
72
+ warning:
73
+ "When restoring, you will see broken previews. Use the admin panel to regenerate them.",
74
+ warningColor: "warning",
75
+ },
76
+ ];
77
+
78
+ const integrations = useIntegrations();
79
+ const repositories = useRepositories();
80
+ const schedules = useSchedules();
81
+ const mutation = useConfigureImmichIntegration();
82
+
83
+ const { onIntegrationUpdate } = useIntegrationEventHandler();
84
+ const { onRepositoryCreate, onRepositoryUpdate, onRepositoryDelete } =
85
+ useRepositoryEventHandler();
86
+ const { onScheduleCreate, onScheduleUpdate, onScheduleDelete } =
87
+ useScheduleEventHandler();
88
+
89
+ const integration = $derived(integrations.data?.immichIntegration);
90
+
91
+ const repository = $derived(
92
+ integration
93
+ ? repositories.data?.find((entry) => entry.id === integration.id)
94
+ : undefined,
95
+ );
96
+
97
+ const schedule = $derived(
98
+ integration
99
+ ? schedules.data?.find((entry) => entry.id === integration.scheduleId)
100
+ : undefined,
101
+ );
102
+
103
+ const isLoading = $derived(
104
+ integrations.isLoading || repositories.isLoading || schedules.isLoading,
105
+ );
106
+
107
+ type LibrariesMode = "all" | "none" | "some";
108
+
109
+ const libraryModes = [
110
+ { value: "all", label: "All libraries" },
111
+ { value: "none", label: "No libraries" },
112
+ { value: "some", label: "Choose libraries" },
113
+ ];
114
+
115
+ const folders = new SvelteSet<string>();
116
+ const selectedLibraries = new SvelteSet<string>();
117
+ let backupConfiguration = $state(true);
118
+ let librariesMode = $state<LibrariesMode>("all");
119
+ let loaded = false;
120
+
121
+ $effect(() => {
122
+ if (loaded || !integration) {
123
+ return;
124
+ }
125
+
126
+ loaded = true;
127
+ backupConfiguration = integration.configuration.backupConfiguration;
128
+
129
+ const libraries = integration.configuration.libraries;
130
+
131
+ if (libraries === "all") {
132
+ librariesMode = "all";
133
+ } else if (libraries.length === 0) {
134
+ librariesMode = "none";
135
+ } else {
136
+ librariesMode = "some";
137
+
138
+ for (const id of libraries) {
139
+ selectedLibraries.add(id);
140
+ }
141
+ }
142
+
143
+ for (const folder of integration.configuration.dataFolders) {
144
+ folders.add(folder);
145
+ }
146
+ });
147
+
148
+ const toggleLibrary = (id: string) => {
149
+ if (selectedLibraries.has(id)) {
150
+ selectedLibraries.delete(id);
151
+ } else {
152
+ selectedLibraries.add(id);
153
+ }
154
+ };
155
+
156
+ const available = $derived(
157
+ integrations.data?.immichState?.dataFolders ?? [],
158
+ );
159
+
160
+ const libraries = $derived(integrations.data?.immichState?.libraries ?? []);
161
+
162
+ const applicable = (group: Group) =>
163
+ group.folders.filter((folder) => available.includes(folder));
164
+
165
+ const isChecked = (group: Group) => {
166
+ const own = applicable(group);
167
+
168
+ return own.length > 0 && own.every((folder) => folders.has(folder));
169
+ };
170
+
171
+ const toggle = (group: Group) => {
172
+ const own = applicable(group);
173
+ const enabled = own.every((folder) => folders.has(folder));
174
+
175
+ for (const folder of own) {
176
+ if (enabled) {
177
+ folders.delete(folder);
178
+ } else {
179
+ folders.add(folder);
180
+ }
181
+ }
182
+ };
183
+
184
+ const onSave = () => {
185
+ folders.add("backups");
186
+
187
+ if (!integration || !repository || !schedule) {
188
+ return;
189
+ }
190
+
191
+ mutation.mutate({
192
+ name: repository.name,
193
+ worm: repository.worm,
194
+ cron: schedule.cron,
195
+ retentionPolicy: repository.configuration?.retentionPolicy ?? null,
196
+ dataFolders: [...folders],
197
+ backupConfiguration,
198
+ libraries:
199
+ librariesMode === "all"
200
+ ? "all"
201
+ : librariesMode === "none"
202
+ ? []
203
+ : [...selectedLibraries],
204
+ });
205
+ };
206
+ </script>
207
+
208
+ <OnEvents
209
+ {onIntegrationUpdate}
210
+ {onRepositoryCreate}
211
+ {onRepositoryUpdate}
212
+ {onRepositoryDelete}
213
+ {onScheduleCreate}
214
+ {onScheduleUpdate}
215
+ {onScheduleDelete}
216
+ />
217
+
218
+ <Accordion
219
+ title="Backup Contents"
220
+ subtitle="Choose what Immich includes in each backup."
221
+ icon={mdiImageMultiple}
222
+ >
223
+ <Stack gap={4} class="pt-2">
224
+ {#if isLoading}
225
+ <LoadingSpinner />
226
+ {:else}
227
+ {#each groups as group (group.key)}
228
+ {#if applicable(group).length > 0}
229
+ <Stack gap={1}>
230
+ <Field
231
+ label={group.label}
232
+ description={group.description}
233
+ readOnly={group.required}
234
+ required={group.required ? "indicator" : undefined}
235
+ >
236
+ <Switch
237
+ checked={group.required || isChecked(group)}
238
+ class={group.required
239
+ ? "cursor-not-allowed opacity-38"
240
+ : undefined}
241
+ onCheckedChange={() => toggle(group)}
242
+ />
243
+ </Field>
244
+
245
+ {#if group.warning && !group.required && !isChecked(group)}
246
+ <Text size="small" color={group.warningColor}>
247
+ {group.warning}
248
+ </Text>
249
+ {/if}
250
+ </Stack>
251
+ {/if}
252
+ {/each}
253
+
254
+ <Stack gap={1}>
255
+ <Field
256
+ label="Backup configuration"
257
+ description="Include these backup settings, so they survive a restore."
258
+ >
259
+ <Switch bind:checked={backupConfiguration} />
260
+ </Field>
261
+
262
+ {#if !backupConfiguration}
263
+ <Text size="small" color="warning">
264
+ You will need to reconfigure backups from scratch after restoring.
265
+ </Text>
266
+ {/if}
267
+ </Stack>
268
+
269
+ <Field
270
+ label="External libraries"
271
+ description="Libraries stored outside of Immich. New libraries are included automatically unless you choose specific ones."
272
+ >
273
+ <Select
274
+ options={libraryModes}
275
+ value={librariesMode}
276
+ onChange={(value) => (librariesMode = value as LibrariesMode)}
277
+ />
278
+ </Field>
279
+
280
+ {#if librariesMode === "some"}
281
+ {#if libraries.length === 0}
282
+ <Text size="small" color="muted">No external libraries found.</Text>
283
+ {:else}
284
+ <Stack gap={2} class="ps-1">
285
+ {#each libraries as library (library.id)}
286
+ <label class="flex select-none items-center gap-3">
287
+ <Checkbox
288
+ checked={selectedLibraries.has(library.id)}
289
+ onCheckedChange={() => toggleLibrary(library.id)}
290
+ />
291
+ <Text size="small">{library.name}</Text>
292
+ </label>
293
+ {/each}
294
+ </Stack>
295
+ {/if}
296
+ {:else if librariesMode === "all" && libraries.length > 0}
297
+ <Text size="small" color="muted">
298
+ Included: {libraries.map((library) => library.name).join(", ")}
299
+ </Text>
300
+ {/if}
301
+
302
+ <HStack class="justify-end">
303
+ <Button
304
+ shape="round"
305
+ loading={mutation.isPending}
306
+ onclick={onSave}
307
+ >
308
+ Save
309
+ </Button>
310
+ </HStack>
311
+ {/if}
312
+ </Stack>
313
+ </Accordion>
@@ -0,0 +1,3 @@
1
+ declare const ImmichSettingsBackupContents: import("svelte").Component<Record<string, never>, {}, "">;
2
+ type ImmichSettingsBackupContents = ReturnType<typeof ImmichSettingsBackupContents>;
3
+ export default ImmichSettingsBackupContents;