@futo-org/backups-orchestrator-ui 0.30.1 → 0.31.1

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.
@@ -64,7 +64,7 @@
64
64
  {backend.description ?? BackendDescriptions[backend.type]}
65
65
 
66
66
  {#snippet trailing()}
67
- <Badge color={online ? "primary" : "danger"} size="small">
67
+ <Badge color={online ? "success" : "danger"} size="small">
68
68
  {#if !backend.isOnline}
69
69
  Offline
70
70
  {:else if repositoryBackend}
@@ -0,0 +1,55 @@
1
+ <script lang="ts">
2
+ import { useRollbackSnapshot } from "../../../services/snapshot.service";
3
+ import {
4
+ Button,
5
+ HStack,
6
+ Modal,
7
+ ModalBody,
8
+ ModalFooter,
9
+ Stack,
10
+ Text,
11
+ } from "@immich/ui";
12
+
13
+ type Props = {
14
+ repository: string;
15
+ snapshot: string;
16
+ onClose: () => void;
17
+ };
18
+
19
+ const { repository, snapshot, onClose }: Props = $props();
20
+
21
+ const mutation = useRollbackSnapshot();
22
+
23
+ const onConfirm = () =>
24
+ mutation.mutate(
25
+ { repositoryId: repository, snapshotId: snapshot },
26
+ { onSuccess: () => onClose() },
27
+ );
28
+ </script>
29
+
30
+ <Modal title="Rollback to this snapshot?" size="small" {onClose}>
31
+ <ModalBody>
32
+ <Stack gap={4}>
33
+ <Text>
34
+ Your instance will return to how it was when this snapshot was taken.
35
+ </Text>
36
+
37
+ <Stack gap={2}>
38
+ <Text>This will:</Text>
39
+ <ul class="list-disc ps-6">
40
+ <li><Text>Restore files from the snapshot</Text></li>
41
+ <li><Text>Restore the Immich database</Text></li>
42
+ <li><Text>Restart the server during rollback</Text></li>
43
+ </ul>
44
+ </Stack>
45
+ </Stack>
46
+ </ModalBody>
47
+ <ModalFooter>
48
+ <HStack>
49
+ <Button color="danger" loading={mutation.isPending} onclick={onConfirm}>
50
+ Confirm rollback
51
+ </Button>
52
+ <Button variant="ghost" onclick={onClose}>Cancel</Button>
53
+ </HStack>
54
+ </ModalFooter>
55
+ </Modal>
@@ -0,0 +1,8 @@
1
+ type Props = {
2
+ repository: string;
3
+ snapshot: string;
4
+ onClose: () => void;
5
+ };
6
+ declare const RollbackSnapshotModal: import("svelte").Component<Props, {}, "">;
7
+ type RollbackSnapshotModal = ReturnType<typeof RollbackSnapshotModal>;
8
+ export default RollbackSnapshotModal;
@@ -0,0 +1,52 @@
1
+ <script lang="ts">
2
+ import OnEvents from "../../util/OnEvents.svelte";
3
+ import {
4
+ useIntegrationEventHandler,
5
+ useIntegrations,
6
+ } from "../../../services/integrations.service";
7
+ import { page } from "$app/state";
8
+ import { Icon, Text } from "@immich/ui";
9
+ import { mdiCloudUpload, mdiCloudUploadOutline } from "@mdi/js";
10
+
11
+ type Props = {
12
+ href: string;
13
+ };
14
+
15
+ const { href }: Props = $props();
16
+
17
+ const active = $derived(page.url.pathname.startsWith(href));
18
+
19
+ const integrations = useIntegrations();
20
+ const { onIntegrationUpdate } = useIntegrationEventHandler();
21
+
22
+ const configured = $derived(
23
+ integrations.data ? Boolean(integrations.data.immichIntegration) : true,
24
+ );
25
+
26
+ const shell = $derived(
27
+ configured
28
+ ? `hover:bg-subtle hover:text-primary flex w-full cursor-pointer place-items-center gap-4 rounded-e-full ps-5 py-3 text-start ${active ? "bg-primary/10 text-primary" : ""}`
29
+ : "border-primary/40 from-white via-blue-100 to-purple-100 dark:from-transparent dark:via-blue-500/10 dark:to-purple-500/15 flex w-full cursor-pointer place-items-center gap-4 rounded-xl border bg-gradient-to-r px-4 py-2.5 text-start",
30
+ );
31
+ </script>
32
+
33
+ <OnEvents {onIntegrationUpdate} />
34
+
35
+ <div class={configured ? "" : "mx-4 mb-2"}>
36
+ <a {href} class={shell}>
37
+ <Icon
38
+ icon={configured ? mdiCloudUploadOutline : mdiCloudUpload}
39
+ size="1.375em"
40
+ class="{configured ? '' : 'text-primary'} shrink-0"
41
+ />
42
+
43
+ <Text
44
+ size="small"
45
+ fontWeight="medium"
46
+ color={configured && !active ? undefined : "primary"}
47
+ class="flex-1 truncate"
48
+ >
49
+ {configured ? "Backups" : "Set up Backups"}
50
+ </Text>
51
+ </a>
52
+ </div>
@@ -0,0 +1,6 @@
1
+ type Props = {
2
+ href: string;
3
+ };
4
+ declare const ImmichBackupsAdminNavButton: import("svelte").Component<Props, {}, "">;
5
+ type ImmichBackupsAdminNavButton = ReturnType<typeof ImmichBackupsAdminNavButton>;
6
+ export default ImmichBackupsAdminNavButton;
@@ -1,5 +1,6 @@
1
1
  <script lang="ts">
2
2
  import OnEvents from "../../util/OnEvents.svelte";
3
+ import RelativeTime from "../../util/RelativeTime.svelte";
3
4
  import {
4
5
  useIntegrationEventHandler,
5
6
  useIntegrations,
@@ -16,15 +17,22 @@
16
17
  useScheduleEventHandler,
17
18
  useSchedules,
18
19
  } from "../../../services/schedule.service";
19
- import ImmichBackupsCard from "./ImmichBackupsCard.svelte";
20
+ import { HStack, Icon, Text } from "@immich/ui";
21
+ import {
22
+ mdiChevronRight,
23
+ mdiCloudAlertOutline,
24
+ mdiCloudCheckVariantOutline,
25
+ mdiCloudOffOutline,
26
+ mdiCloudUploadOutline,
27
+ } from "@mdi/js";
28
+
29
+ type Color = "primary" | "success" | "warning" | "danger";
20
30
 
21
31
  type Props = {
22
- href?: string;
23
- onclick?: () => void;
24
- class?: string;
32
+ href: string;
25
33
  };
26
34
 
27
- const { href = "/backups", onclick, class: className }: Props = $props();
35
+ const { href }: Props = $props();
28
36
 
29
37
  // TODO: this should probably be condensed into one big request - since this loads on every initial Immich page load (in the future)
30
38
 
@@ -74,6 +82,36 @@
74
82
  const loading = $derived(
75
83
  integrations.isLoading || repositories.isLoading || schedules.isLoading,
76
84
  );
85
+
86
+ const configured = $derived(Boolean(repository));
87
+ const running = $derived(latestBackupRun?.status === "incomplete");
88
+
89
+ const status = $derived.by(() => {
90
+ if (running) {
91
+ return { color: "primary", icon: mdiCloudUploadOutline } as const;
92
+ }
93
+
94
+ if (failed) {
95
+ return { color: "danger", icon: mdiCloudOffOutline } as const;
96
+ }
97
+
98
+ if (paused) {
99
+ return { color: "warning", icon: mdiCloudAlertOutline } as const;
100
+ }
101
+
102
+ if (!configured || !lastBackup) {
103
+ return { color: "warning", icon: mdiCloudOffOutline } as const;
104
+ }
105
+
106
+ return { color: "success", icon: mdiCloudCheckVariantOutline } as const;
107
+ });
108
+
109
+ const tints: Record<Color, string> = {
110
+ primary: "bg-primary-50 text-primary",
111
+ success: "bg-success-50 text-success-700",
112
+ warning: "bg-warning-50 text-warning-800",
113
+ danger: "bg-danger-50 text-danger-700",
114
+ };
77
115
  </script>
78
116
 
79
117
  <OnEvents
@@ -89,14 +127,41 @@
89
127
  />
90
128
 
91
129
  {#if !loading}
92
- <ImmichBackupsCard
130
+ <a
93
131
  {href}
94
- {failed}
95
- {paused}
96
- {onclick}
97
- {lastBackup}
98
- class={className}
99
- configured={Boolean(repository)}
100
- running={latestBackupRun?.status === "incomplete"}
101
- />
132
+ class="flex w-full cursor-pointer items-center gap-2 px-3 py-3 text-start text-sm {tints[
133
+ status.color
134
+ ]}"
135
+ >
136
+ <Icon color={status.color} icon={status.icon} size="1.25em" class="shrink-0" />
137
+
138
+ <Text size="tiny" color={status.color} class="flex-1 truncate">
139
+ {#if running}
140
+ Backing up now
141
+ {:else if failed}
142
+ Backup failed
143
+ {:else if paused}
144
+ Backups paused
145
+ {:else if !configured}
146
+ Not backed up
147
+ {:else if lastBackup}
148
+ Last backup <RelativeTime time={lastBackup} />
149
+ {:else}
150
+ Finish setting up backups
151
+ {/if}
152
+ </Text>
153
+
154
+ <HStack gap={0}>
155
+ {#if !configured}
156
+ <Text size="tiny" color="primary" class="shrink-0">Set up</Text>
157
+ {/if}
158
+
159
+ <Icon
160
+ color={configured ? status.color : "primary5"}
161
+ icon={mdiChevronRight}
162
+ size="1.25em"
163
+ class="shrink-0"
164
+ />
165
+ </HStack>
166
+ </a>
102
167
  {/if}
@@ -1,7 +1,5 @@
1
1
  type Props = {
2
- href?: string;
3
- onclick?: () => void;
4
- class?: string;
2
+ href: string;
5
3
  };
6
4
  declare const ImmichBackupsSidebarItem: import("svelte").Component<Props, {}, "">;
7
5
  type ImmichBackupsSidebarItem = ReturnType<typeof ImmichBackupsSidebarItem>;
@@ -36,20 +36,15 @@
36
36
  } from "@mdi/js";
37
37
  import { options } from "../../options";
38
38
  import { onDestroy } from "svelte";
39
- import ImmichBackupsNavButton from "../integrations/immich/ImmichBackupsNavButton.svelte";
40
- import ImmichBackupsCard from "../integrations/immich/ImmichBackupsCard.svelte";
39
+ import ImmichBackupsAdminNavButton from "../integrations/immich/ImmichBackupsAdminNavButton.svelte";
40
+ import ImmichBackupsSidebarItem from "../integrations/immich/ImmichBackupsSidebarItem.svelte";
41
41
  import MockImmichPhotos from "./immich/MockImmichPhotos.svelte";
42
42
  import MockImmichServerStatus from "./immich/MockImmichServerStatus.svelte";
43
43
  import MockImmichPurchaseInfo from "./immich/MockImmichPurchaseInfo.svelte";
44
44
  import MockImmichStorageSpace from "./immich/MockImmichStorageSpace.svelte";
45
45
  import ImmichBackupsPage from "../integrations/immich/ImmichBackupsPage.svelte";
46
46
  import ImmichOnboardingRestoreFlow from "../integrations/immich/ImmichOnboardingRestoreFlow.svelte";
47
- import OnEvents from "../util/OnEvents.svelte";
48
47
  import MockImmichHideBackupsReminder from "./immich/MockImmichHideBackupsReminder.svelte";
49
- import {
50
- useIntegrationEventHandler,
51
- useIntegrations,
52
- } from "../../services/integrations.service";
53
48
 
54
49
  type Props = {
55
50
  onExit: () => void;
@@ -60,11 +55,6 @@
60
55
 
61
56
  let route = $state<"photos" | "settings">("photos");
62
57
 
63
- const integrations = useIntegrations();
64
- const { onIntegrationUpdate } = useIntegrationEventHandler();
65
- const configured = $derived(Boolean(integrations.data?.immichIntegration));
66
- const twoHoursAgo = new Date(Date.now() - 2 * 60 * 60 * 1000).toISOString();
67
-
68
58
  const sampleQuestions = [
69
59
  {
70
60
  title: "Is FUTO Backups the same as an Immich product key?",
@@ -87,8 +77,6 @@
87
77
  setProvider(orchestrationApiProvider);
88
78
  </script>
89
79
 
90
- <OnEvents {onIntegrationUpdate} />
91
-
92
80
  {#snippet hideReminder()}
93
81
  <MockImmichHideBackupsReminder />
94
82
  {/snippet}
@@ -203,11 +191,7 @@
203
191
  <AppShellSidebar class="relative">
204
192
  <div class="flex h-full flex-col pt-4 pr-2">
205
193
  {#if route === "settings"}
206
- <ImmichBackupsNavButton
207
- {configured}
208
- active
209
- onclick={() => {}}
210
- />
194
+ <ImmichBackupsAdminNavButton href="#" />
211
195
 
212
196
  <NavbarItem
213
197
  href="#"
@@ -260,11 +244,7 @@
260
244
 
261
245
  <Stack gap={4} class="mt-auto p-4">
262
246
  <MockImmichStorageSpace>
263
- <ImmichBackupsCard
264
- {configured}
265
- lastBackup={configured ? twoHoursAgo : undefined}
266
- onclick={() => (route = "settings")}
267
- />
247
+ <ImmichBackupsSidebarItem href="#" />
268
248
  </MockImmichStorageSpace>
269
249
 
270
250
  {#if route !== "settings"}
@@ -1,17 +1,20 @@
1
1
  <script lang="ts">
2
- import StackList from "./StackList.svelte";
3
- import StackListItem from "./StackListItem.svelte";
4
2
  import type { FilesystemListingResponseDto } from "../../fetch-client";
5
3
  import {
6
4
  Button,
5
+ Heading,
6
+ HStack,
7
7
  IconButton,
8
8
  modalManager,
9
+ Stack,
9
10
  Text,
10
11
  } from "@immich/ui";
11
12
  import { mdiClose } from "@mdi/js";
12
13
  import type { Snippet } from "svelte";
13
14
  import type { SvelteSet } from "svelte/reactivity";
14
15
  import PathPickerModal from "./PathPickerModal.svelte";
16
+ import StackList from "./StackList.svelte";
17
+ import StackListItem from "./StackListItem.svelte";
15
18
 
16
19
  type Props = {
17
20
  paths: SvelteSet<string>;
@@ -51,36 +54,34 @@
51
54
  });
52
55
  </script>
53
56
 
54
- <StackList>
55
- {#snippet title()}
56
- {@render label?.()}
57
- {/snippet}
57
+ <Stack>
58
+ <Heading size="tiny">{@render label?.()}</Heading>
58
59
 
59
60
  {#if paths.size > 0}
60
- {#each [...paths] as path (path)}
61
- <StackListItem>
62
- <Text class="grow truncate" title={path}>{path}</Text>
61
+ <StackList>
62
+ {#each [...paths] as path (path)}
63
+ <StackListItem>
64
+ <Text class="grow truncate" title={path}>{path}</Text>
63
65
 
64
- {#snippet trailing()}
65
- <IconButton
66
- icon={mdiClose}
67
- aria-label="Remove"
68
- size="tiny"
69
- variant="ghost"
70
- onclick={() => paths.delete(path)}
71
- />
72
- {/snippet}
73
- </StackListItem>
74
- {/each}
66
+ {#snippet trailing()}
67
+ <IconButton
68
+ icon={mdiClose}
69
+ aria-label="Remove"
70
+ size="tiny"
71
+ variant="ghost"
72
+ onclick={() => paths.delete(path)}
73
+ />
74
+ {/snippet}
75
+ </StackListItem>
76
+ {/each}
77
+ </StackList>
75
78
  {:else if empty}
76
- <StackListItem>
79
+ <HStack>
77
80
  <Text color="secondary">{@render empty()}</Text>
78
- </StackListItem>
81
+ </HStack>
79
82
  {/if}
80
83
 
81
- <StackListItem>
82
- <Button size="small" variant="ghost" onclick={openPicker}>
83
- {paths.size > 0 ? addLabel : manageLabel}
84
- </Button>
85
- </StackListItem>
86
- </StackList>
84
+ <Button class="w-fit" size="small" variant="ghost" onclick={openPicker}>
85
+ {paths.size > 0 ? addLabel : manageLabel}
86
+ </Button>
87
+ </Stack>
package/dist/index.d.ts CHANGED
@@ -2,7 +2,7 @@ export { default as BackendsList } from './components/backends/BackendsList.svel
2
2
  export { default as BackupsList } from './components/backups/BackupsList.svelte';
3
3
  export { default as ViewStatusModal } from './components/backups/dialogs/ViewStatusModal.svelte';
4
4
  export { default as Dashboard } from './components/dashboard/Dashboard.svelte';
5
- export { default as ImmichBackupsNavButton } from './components/integrations/immich/ImmichBackupsNavButton.svelte';
5
+ export { default as ImmichBackupsAdminNavButton } from './components/integrations/immich/ImmichBackupsAdminNavButton.svelte';
6
6
  export { default as ImmichBackupsPage } from './components/integrations/immich/ImmichBackupsPage.svelte';
7
7
  export { default as ImmichBackupsSidebarItem } from './components/integrations/immich/ImmichBackupsSidebarItem.svelte';
8
8
  export { default as ImmichManageBackup } from './components/integrations/immich/ImmichManageBackup.svelte';
package/dist/index.js CHANGED
@@ -2,7 +2,7 @@ export { default as BackendsList } from './components/backends/BackendsList.svel
2
2
  export { default as BackupsList } from './components/backups/BackupsList.svelte';
3
3
  export { default as ViewStatusModal } from './components/backups/dialogs/ViewStatusModal.svelte';
4
4
  export { default as Dashboard } from './components/dashboard/Dashboard.svelte';
5
- export { default as ImmichBackupsNavButton } from './components/integrations/immich/ImmichBackupsNavButton.svelte';
5
+ export { default as ImmichBackupsAdminNavButton } from './components/integrations/immich/ImmichBackupsAdminNavButton.svelte';
6
6
  export { default as ImmichBackupsPage } from './components/integrations/immich/ImmichBackupsPage.svelte';
7
7
  export { default as ImmichBackupsSidebarItem } from './components/integrations/immich/ImmichBackupsSidebarItem.svelte';
8
8
  export { default as ImmichManageBackup } from './components/integrations/immich/ImmichManageBackup.svelte';
@@ -1,5 +1,6 @@
1
1
  import { sdk } from '..';
2
2
  import RestoreSnapshotModal from '../components/backups/dialogs/RestoreSnapshotModal.svelte';
3
+ import RollbackSnapshotModal from '../components/backups/dialogs/RollbackSnapshotModal.svelte';
3
4
  import { SocketEvent } from '../events';
4
5
  import { getSnapshots, } from '../fetch-client';
5
6
  import { queryClient } from '../query-client';
@@ -83,9 +84,8 @@ export const handleGetSnapshotListing = async (id, snapshotId, path) => {
83
84
  };
84
85
  export const getSnapshotActions = (repositoryId, snapshot, immich) => {
85
86
  const removeSnapshot = useRemoveSnapshot(repositoryId);
86
- const rollback = useRollbackSnapshot();
87
87
  const Restore = {
88
- title: 'Restore',
88
+ title: 'Restore files',
89
89
  icon: mdiBackupRestore,
90
90
  onAction: () => void modalManager.open(RestoreSnapshotModal, {
91
91
  repository: repositoryId,
@@ -93,9 +93,12 @@ export const getSnapshotActions = (repositoryId, snapshot, immich) => {
93
93
  }),
94
94
  };
95
95
  const Rollback = {
96
- title: 'Rollback',
96
+ title: 'Rollback snapshot',
97
97
  icon: mdiHistory,
98
- onAction: () => rollback.mutate({ repositoryId, snapshotId: snapshot.id }),
98
+ onAction: () => void modalManager.open(RollbackSnapshotModal, {
99
+ repository: repositoryId,
100
+ snapshot: snapshot.id,
101
+ }),
99
102
  $if: () => immich === true,
100
103
  };
101
104
  const Delete = {
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@futo-org/backups-orchestrator-ui",
3
3
  "repository": "https://github.com/immich-app/yucca",
4
4
  "description": "Backups orchestrator (UI library)",
5
- "version": "0.30.1",
5
+ "version": "0.31.1",
6
6
  "license": "Source First License 1.1",
7
7
  "files": [
8
8
  "dist",
@@ -64,7 +64,7 @@
64
64
  "lodash.debounce": "^4.0.8",
65
65
  "luxon": "^3.7.2",
66
66
  "socket.io-client": "^4.8.3",
67
- "@futo-org/backups-api-client": "^0.30.1"
67
+ "@futo-org/backups-api-client": "^0.31.1"
68
68
  },
69
69
  "scripts": {
70
70
  "dev": "vite dev --port 5174",
@@ -1,109 +0,0 @@
1
- <script lang="ts">
2
- import RelativeTime from "../../util/RelativeTime.svelte";
3
- import { Icon, Text } from "@immich/ui";
4
- import {
5
- mdiChevronRight,
6
- mdiCloudAlertOutline,
7
- mdiCloudCheckVariantOutline,
8
- mdiCloudOffOutline,
9
- mdiCloudUploadOutline,
10
- } from "@mdi/js";
11
-
12
- type Color = "primary" | "success" | "warning" | "danger";
13
-
14
- type Props = {
15
- configured: boolean;
16
- lastBackup?: string;
17
- failed?: boolean;
18
- paused?: boolean;
19
- running?: boolean;
20
- href?: string;
21
- onclick?: () => void;
22
- class?: string;
23
- };
24
-
25
- const {
26
- configured,
27
- lastBackup,
28
- failed = false,
29
- paused = false,
30
- running = false,
31
- href,
32
- onclick,
33
- class: className,
34
- }: Props = $props();
35
-
36
- const status = $derived.by(() => {
37
- if (running) {
38
- return { color: "primary", icon: mdiCloudUploadOutline } as const;
39
- }
40
-
41
- if (failed) {
42
- return { color: "danger", icon: mdiCloudOffOutline } as const;
43
- }
44
-
45
- if (paused) {
46
- return { color: "warning", icon: mdiCloudAlertOutline } as const;
47
- }
48
-
49
- if (!configured || !lastBackup) {
50
- return { color: "warning", icon: mdiCloudOffOutline } as const;
51
- }
52
-
53
- return { color: "success", icon: mdiCloudCheckVariantOutline } as const;
54
- });
55
-
56
- const tints: Record<Color, string> = {
57
- primary: "bg-primary-50 text-primary",
58
- success: "bg-success-50 text-success-700",
59
- warning: "bg-warning-50 text-warning-800",
60
- danger: "bg-danger-50 text-danger-700",
61
- };
62
-
63
- const shell = $derived(
64
- `flex w-full cursor-pointer items-center gap-2 px-3 py-3 text-start text-sm ${tints[status.color]} ${className ?? ""}`,
65
- );
66
- </script>
67
-
68
- {#snippet body()}
69
- <Icon icon={status.icon} size="1.25em" class="shrink-0" />
70
-
71
- <Text
72
- size="small"
73
- color={status.color}
74
- fontWeight="medium"
75
- class="flex-1 truncate"
76
- >
77
- {#if running}
78
- Backing up now
79
- {:else if failed}
80
- Backup failed
81
- {:else if paused}
82
- Backups paused
83
- {:else if !configured}
84
- Not backed up
85
- {:else if lastBackup}
86
- Last backup <RelativeTime time={lastBackup} />
87
- {:else}
88
- Finish setting up backups
89
- {/if}
90
- </Text>
91
-
92
- {#if !configured}
93
- <Text size="small" color="primary" fontWeight="medium" class="shrink-0">
94
- Set up
95
- </Text>
96
- {/if}
97
-
98
- <Icon icon={mdiChevronRight} size="1.25em" class="shrink-0" />
99
- {/snippet}
100
-
101
- {#if href}
102
- <a {href} {onclick} class={shell}>
103
- {@render body()}
104
- </a>
105
- {:else}
106
- <button type="button" {onclick} class={shell}>
107
- {@render body()}
108
- </button>
109
- {/if}
@@ -1,13 +0,0 @@
1
- type Props = {
2
- configured: boolean;
3
- lastBackup?: string;
4
- failed?: boolean;
5
- paused?: boolean;
6
- running?: boolean;
7
- href?: string;
8
- onclick?: () => void;
9
- class?: string;
10
- };
11
- declare const ImmichBackupsCard: import("svelte").Component<Props, {}, "">;
12
- type ImmichBackupsCard = ReturnType<typeof ImmichBackupsCard>;
13
- export default ImmichBackupsCard;
@@ -1,55 +0,0 @@
1
- <script lang="ts">
2
- import { Icon, Text } from "@immich/ui";
3
- import { mdiCloudUpload, mdiCloudUploadOutline } from "@mdi/js";
4
-
5
- type Props = {
6
- configured: boolean;
7
- active?: boolean;
8
- href?: string;
9
- onclick?: () => void;
10
- class?: string;
11
- };
12
-
13
- const {
14
- configured,
15
- active = false,
16
- href,
17
- onclick,
18
- class: className,
19
- }: Props = $props();
20
-
21
- const shell = $derived(
22
- configured
23
- ? `hover:bg-subtle hover:text-primary flex w-full cursor-pointer place-items-center gap-4 rounded-e-full ps-5 py-3 text-start ${active ? "bg-primary/10 text-primary" : ""}`
24
- : "border-primary/40 from-white via-blue-100 to-purple-100 dark:from-transparent dark:via-blue-500/10 dark:to-purple-500/15 flex w-full cursor-pointer place-items-center gap-4 rounded-xl border bg-gradient-to-r px-4 py-2.5 text-start",
25
- );
26
- </script>
27
-
28
- {#snippet body()}
29
- <Icon
30
- icon={configured ? mdiCloudUploadOutline : mdiCloudUpload}
31
- size="1.375em"
32
- class="{configured ? '' : 'text-primary'} shrink-0"
33
- />
34
-
35
- <Text
36
- size="small"
37
- fontWeight="medium"
38
- color={configured && !active ? undefined : "primary"}
39
- class="flex-1 truncate"
40
- >
41
- {configured ? "Backups" : "Set up Backups"}
42
- </Text>
43
- {/snippet}
44
-
45
- <div class="{configured ? '' : 'mx-4 mb-2'} {className ?? ''}">
46
- {#if href}
47
- <a {href} {onclick} class={shell}>
48
- {@render body()}
49
- </a>
50
- {:else}
51
- <button type="button" {onclick} class={shell}>
52
- {@render body()}
53
- </button>
54
- {/if}
55
- </div>
@@ -1,10 +0,0 @@
1
- type Props = {
2
- configured: boolean;
3
- active?: boolean;
4
- href?: string;
5
- onclick?: () => void;
6
- class?: string;
7
- };
8
- declare const ImmichBackupsNavButton: import("svelte").Component<Props, {}, "">;
9
- type ImmichBackupsNavButton = ReturnType<typeof ImmichBackupsNavButton>;
10
- export default ImmichBackupsNavButton;