@futo-org/backups-orchestrator-ui 0.28.0 → 0.30.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.
- package/dist/components/backups/run-history/RepositoryRunHistory.svelte +2 -3
- package/dist/components/backups/run-history/RepositoryRunHistoryPage.svelte +4 -3
- package/dist/components/integrations/immich/ImmichBackupsCard.svelte +79 -43
- package/dist/components/integrations/immich/ImmichBackupsCard.svelte.d.ts +2 -1
- package/dist/components/integrations/immich/ImmichBackupsPage.svelte +2 -1
- package/dist/components/integrations/immich/ImmichBackupsPage.svelte.d.ts +2 -1
- package/dist/components/integrations/immich/ImmichBackupsSidebarItem.svelte +48 -12
- package/dist/components/integrations/immich/settings/ImmichSettingsStorage.svelte +1 -1
- package/dist/components/onboarding/upsell/UpsellFrequentlyAskedQuestions.svelte +7 -2
- package/dist/components/onboarding/upsell/UpsellFrequentlyAskedQuestions.svelte.d.ts +2 -1
- package/dist/components/onboarding/upsell/UpsellPage.svelte +2 -1
- package/dist/components/onboarding/upsell/UpsellPage.svelte.d.ts +2 -1
- package/dist/components/test/ImmichTestUi.svelte +23 -9
- package/dist/components/test/immich/MockImmichHideBackupsReminder.svelte +12 -0
- package/dist/components/test/immich/MockImmichHideBackupsReminder.svelte.d.ts +3 -0
- package/dist/components/test/immich/MockImmichServerStatus.svelte +9 -0
- package/dist/components/test/immich/MockImmichServerStatus.svelte.d.ts +18 -0
- package/dist/components/test/immich/MockImmichSidebar.svelte +20 -0
- package/dist/components/test/immich/MockImmichSidebar.svelte.d.ts +7 -0
- package/dist/components/test/immich/MockImmichStorageSpace.svelte +14 -26
- package/dist/components/test/immich/MockImmichStorageSpace.svelte.d.ts +2 -1
- package/dist/services/runHistory.service.d.ts +1 -1
- package/dist/services/runHistory.service.js +3 -2
- package/package.json +2 -2
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import StackList from "../../ui/StackList.svelte";
|
|
3
3
|
import StackListPlaceholder from "../../ui/StackListPlaceholder.svelte";
|
|
4
|
-
import Suspense from "../../util/Suspense.svelte";
|
|
5
4
|
import OnEvents from "../../util/OnEvents.svelte";
|
|
5
|
+
import Suspense from "../../util/Suspense.svelte";
|
|
6
6
|
import type { LocalRepositoryDto } from "../../../fetch-client";
|
|
7
7
|
import {
|
|
8
8
|
useRunEventHandler,
|
|
@@ -19,8 +19,7 @@
|
|
|
19
19
|
|
|
20
20
|
let { repository, limit = 5, onViewAll }: Props = $props();
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
const query = useRunHistory(repository.id);
|
|
22
|
+
const query = useRunHistory(() => repository.id);
|
|
24
23
|
const { onRunCreate, onRunUpdate } = useRunEventHandler();
|
|
25
24
|
</script>
|
|
26
25
|
|
|
@@ -21,8 +21,7 @@
|
|
|
21
21
|
|
|
22
22
|
let { repository, pageSize = 10 }: Props = $props();
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
const query = useRunHistory(repository.id);
|
|
24
|
+
const query = useRunHistory(() => repository.id);
|
|
26
25
|
const { onRunCreate, onRunUpdate } = useRunEventHandler();
|
|
27
26
|
|
|
28
27
|
let search = $state("");
|
|
@@ -101,7 +100,9 @@
|
|
|
101
100
|
),
|
|
102
101
|
);
|
|
103
102
|
|
|
104
|
-
const pageCount = $derived(
|
|
103
|
+
const pageCount = $derived(
|
|
104
|
+
Math.max(Math.ceil(filtered.length / pageSize), 1),
|
|
105
|
+
);
|
|
105
106
|
const current = $derived(Math.min(page, pageCount));
|
|
106
107
|
const visible = $derived(
|
|
107
108
|
filtered.slice((current - 1) * pageSize, current * pageSize),
|
|
@@ -1,13 +1,22 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import RelativeTime from "../../util/RelativeTime.svelte";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
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";
|
|
5
13
|
|
|
6
14
|
type Props = {
|
|
7
15
|
configured: boolean;
|
|
8
16
|
lastBackup?: string;
|
|
9
17
|
failed?: boolean;
|
|
10
|
-
|
|
18
|
+
paused?: boolean;
|
|
19
|
+
running?: boolean;
|
|
11
20
|
href?: string;
|
|
12
21
|
onclick?: () => void;
|
|
13
22
|
class?: string;
|
|
@@ -17,57 +26,84 @@
|
|
|
17
26
|
configured,
|
|
18
27
|
lastBackup,
|
|
19
28
|
failed = false,
|
|
20
|
-
|
|
29
|
+
paused = false,
|
|
30
|
+
running = false,
|
|
21
31
|
href,
|
|
22
32
|
onclick,
|
|
23
33
|
class: className,
|
|
24
34
|
}: Props = $props();
|
|
25
35
|
|
|
26
|
-
const
|
|
27
|
-
|
|
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
|
+
);
|
|
28
66
|
</script>
|
|
29
67
|
|
|
30
68
|
{#snippet body()}
|
|
31
|
-
<Icon
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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>
|
|
36
91
|
|
|
37
92
|
{#if !configured}
|
|
38
|
-
<Text color="primary" fontWeight="medium" class="
|
|
39
|
-
Set up
|
|
93
|
+
<Text size="small" color="primary" fontWeight="medium" class="shrink-0">
|
|
94
|
+
Set up
|
|
40
95
|
</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
|
-
· <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
96
|
{/if}
|
|
97
|
+
|
|
98
|
+
<Icon icon={mdiChevronRight} size="1.25em" class="shrink-0" />
|
|
61
99
|
{/snippet}
|
|
62
100
|
|
|
63
|
-
|
|
64
|
-
{
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
{
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
{/if}
|
|
73
|
-
</div>
|
|
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,11 +1,12 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
+
import type { Snippet } from "svelte";
|
|
2
3
|
import UpsellPage from "../../onboarding/upsell/UpsellPage.svelte";
|
|
3
4
|
import ImmichManageBackup from "./ImmichManageBackup.svelte";
|
|
4
5
|
import ImmichOnboardingSetupFlow from "./ImmichOnboardingSetupFlow.svelte";
|
|
5
6
|
|
|
6
7
|
type Question = {
|
|
7
8
|
title: string;
|
|
8
|
-
answer: string;
|
|
9
|
+
answer: string | Snippet;
|
|
9
10
|
};
|
|
10
11
|
|
|
11
12
|
type Props = {
|
|
@@ -8,6 +8,14 @@
|
|
|
8
8
|
useRepositories,
|
|
9
9
|
useRepositoryEventHandler,
|
|
10
10
|
} from "../../../services/repository.service";
|
|
11
|
+
import {
|
|
12
|
+
useRunEventHandler,
|
|
13
|
+
useRunHistory,
|
|
14
|
+
} from "../../../services/runHistory.service";
|
|
15
|
+
import {
|
|
16
|
+
useScheduleEventHandler,
|
|
17
|
+
useSchedules,
|
|
18
|
+
} from "../../../services/schedule.service";
|
|
11
19
|
import ImmichBackupsCard from "./ImmichBackupsCard.svelte";
|
|
12
20
|
|
|
13
21
|
type Props = {
|
|
@@ -18,20 +26,42 @@
|
|
|
18
26
|
|
|
19
27
|
const { href = "/backups", onclick, class: className }: Props = $props();
|
|
20
28
|
|
|
29
|
+
// TODO: this should probably be condensed into one big request - since this loads on every initial Immich page load (in the future)
|
|
30
|
+
|
|
21
31
|
const integrations = useIntegrations();
|
|
22
32
|
const repositories = useRepositories();
|
|
33
|
+
const schedules = useSchedules();
|
|
23
34
|
|
|
24
35
|
const { onIntegrationUpdate } = useIntegrationEventHandler();
|
|
25
36
|
const { onRepositoryCreate, onRepositoryUpdate, onRepositoryDelete } =
|
|
26
37
|
useRepositoryEventHandler();
|
|
38
|
+
const { onScheduleCreate, onScheduleUpdate, onScheduleDelete } =
|
|
39
|
+
useScheduleEventHandler();
|
|
40
|
+
const { onRunCreate, onRunUpdate } = useRunEventHandler();
|
|
27
41
|
|
|
28
|
-
const
|
|
29
|
-
const integration = integrations.data?.immichIntegration;
|
|
42
|
+
const integration = $derived(integrations.data?.immichIntegration);
|
|
30
43
|
|
|
31
|
-
|
|
44
|
+
const repository = $derived(
|
|
45
|
+
integration
|
|
32
46
|
? repositories.data?.find((entry) => entry.id === integration.id)
|
|
33
|
-
: undefined
|
|
34
|
-
|
|
47
|
+
: undefined,
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
const runHistory = useRunHistory(() => repository?.id);
|
|
51
|
+
|
|
52
|
+
const latestBackupRun = $derived(
|
|
53
|
+
runHistory.data?.find(
|
|
54
|
+
(run) => run.type === "backup" || run.type === "schedule",
|
|
55
|
+
),
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
const paused = $derived(
|
|
59
|
+
Boolean(
|
|
60
|
+
integration &&
|
|
61
|
+
schedules.data?.find((entry) => entry.id === integration.scheduleId)
|
|
62
|
+
?.paused,
|
|
63
|
+
),
|
|
64
|
+
);
|
|
35
65
|
|
|
36
66
|
const lastBackup = $derived(repository?.metrics.lastBackup ?? undefined);
|
|
37
67
|
|
|
@@ -41,8 +71,8 @@
|
|
|
41
71
|
),
|
|
42
72
|
);
|
|
43
73
|
|
|
44
|
-
const
|
|
45
|
-
|
|
74
|
+
const loading = $derived(
|
|
75
|
+
integrations.isLoading || repositories.isLoading || schedules.isLoading,
|
|
46
76
|
);
|
|
47
77
|
</script>
|
|
48
78
|
|
|
@@ -51,16 +81,22 @@
|
|
|
51
81
|
{onRepositoryCreate}
|
|
52
82
|
{onRepositoryUpdate}
|
|
53
83
|
{onRepositoryDelete}
|
|
84
|
+
{onScheduleCreate}
|
|
85
|
+
{onScheduleUpdate}
|
|
86
|
+
{onScheduleDelete}
|
|
87
|
+
{onRunCreate}
|
|
88
|
+
{onRunUpdate}
|
|
54
89
|
/>
|
|
55
90
|
|
|
56
|
-
{#if !
|
|
91
|
+
{#if !loading}
|
|
57
92
|
<ImmichBackupsCard
|
|
58
|
-
configured={Boolean(repository)}
|
|
59
|
-
{lastBackup}
|
|
60
|
-
{failed}
|
|
61
|
-
{sizeBytes}
|
|
62
93
|
{href}
|
|
94
|
+
{failed}
|
|
95
|
+
{paused}
|
|
63
96
|
{onclick}
|
|
97
|
+
{lastBackup}
|
|
64
98
|
class={className}
|
|
99
|
+
configured={Boolean(repository)}
|
|
100
|
+
running={latestBackupRun?.status === "incomplete"}
|
|
65
101
|
/>
|
|
66
102
|
{/if}
|
|
@@ -158,7 +158,7 @@
|
|
|
158
158
|
description="Once written, backups can't be removed. This can't be turned off again."
|
|
159
159
|
readOnly // @immich/ui forces enabled={false} when disabled={true}
|
|
160
160
|
>
|
|
161
|
-
<Switch checked class="
|
|
161
|
+
<Switch checked class="cursor-not-allowed opacity-38" />
|
|
162
162
|
</Field>
|
|
163
163
|
{:else}
|
|
164
164
|
<Field
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import Accordion from "../../ui/Accordion.svelte";
|
|
3
3
|
import { Stack, Text } from "@immich/ui";
|
|
4
|
+
import type { Snippet } from "svelte";
|
|
4
5
|
|
|
5
6
|
type Question = {
|
|
6
7
|
title: string;
|
|
7
|
-
answer: string;
|
|
8
|
+
answer: string | Snippet;
|
|
8
9
|
};
|
|
9
10
|
|
|
10
11
|
type Props = {
|
|
@@ -20,7 +21,11 @@
|
|
|
20
21
|
<Stack gap={0}>
|
|
21
22
|
{#each questions as { title, answer } (title)}
|
|
22
23
|
<Accordion {title}>
|
|
23
|
-
|
|
24
|
+
{#if typeof answer === "string"}
|
|
25
|
+
<Text size="small" color="muted">{answer}</Text>
|
|
26
|
+
{:else}
|
|
27
|
+
{@render answer()}
|
|
28
|
+
{/if}
|
|
24
29
|
</Accordion>
|
|
25
30
|
{/each}
|
|
26
31
|
</Stack>
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { Card, CardBody, Container, HStack, Stack } from "@immich/ui";
|
|
3
|
+
import type { Snippet } from "svelte";
|
|
3
4
|
import UpsellBackupFeaturesGrid from "./UpsellBackupFeaturesGrid.svelte";
|
|
4
5
|
import UpsellExplainFutoBackups from "./UpsellExplainFutoBackups.svelte";
|
|
5
6
|
import UpsellFrequentlyAskedQuestions from "./UpsellFrequentlyAskedQuestions.svelte";
|
|
@@ -7,7 +8,7 @@
|
|
|
7
8
|
|
|
8
9
|
type Question = {
|
|
9
10
|
title: string;
|
|
10
|
-
answer: string;
|
|
11
|
+
answer: string | Snippet;
|
|
11
12
|
};
|
|
12
13
|
|
|
13
14
|
type Props = {
|
|
@@ -6,14 +6,12 @@
|
|
|
6
6
|
AppShellSidebar,
|
|
7
7
|
Avatar,
|
|
8
8
|
Button,
|
|
9
|
-
HStack,
|
|
10
9
|
IconButton,
|
|
11
10
|
Input,
|
|
12
11
|
Logo,
|
|
13
12
|
NavbarGroup,
|
|
14
13
|
NavbarItem,
|
|
15
14
|
Stack,
|
|
16
|
-
Text,
|
|
17
15
|
ThemeSwitcher,
|
|
18
16
|
} from "@immich/ui";
|
|
19
17
|
import {
|
|
@@ -39,12 +37,15 @@
|
|
|
39
37
|
import { options } from "../../options";
|
|
40
38
|
import { onDestroy } from "svelte";
|
|
41
39
|
import ImmichBackupsNavButton from "../integrations/immich/ImmichBackupsNavButton.svelte";
|
|
40
|
+
import ImmichBackupsCard from "../integrations/immich/ImmichBackupsCard.svelte";
|
|
42
41
|
import MockImmichPhotos from "./immich/MockImmichPhotos.svelte";
|
|
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
47
|
import OnEvents from "../util/OnEvents.svelte";
|
|
48
|
+
import MockImmichHideBackupsReminder from "./immich/MockImmichHideBackupsReminder.svelte";
|
|
48
49
|
import {
|
|
49
50
|
useIntegrationEventHandler,
|
|
50
51
|
useIntegrations,
|
|
@@ -62,6 +63,7 @@
|
|
|
62
63
|
const integrations = useIntegrations();
|
|
63
64
|
const { onIntegrationUpdate } = useIntegrationEventHandler();
|
|
64
65
|
const configured = $derived(Boolean(integrations.data?.immichIntegration));
|
|
66
|
+
const twoHoursAgo = new Date(Date.now() - 2 * 60 * 60 * 1000).toISOString();
|
|
65
67
|
|
|
66
68
|
const sampleQuestions = [
|
|
67
69
|
{
|
|
@@ -87,6 +89,10 @@
|
|
|
87
89
|
|
|
88
90
|
<OnEvents {onIntegrationUpdate} />
|
|
89
91
|
|
|
92
|
+
{#snippet hideReminder()}
|
|
93
|
+
<MockImmichHideBackupsReminder />
|
|
94
|
+
{/snippet}
|
|
95
|
+
|
|
90
96
|
<AppShell class="h-full">
|
|
91
97
|
<AppShellHeader>
|
|
92
98
|
<div class="grid grid-cols-[--spacing(64)_auto] items-center w-full py-2">
|
|
@@ -253,17 +259,19 @@
|
|
|
253
259
|
{/if}
|
|
254
260
|
|
|
255
261
|
<Stack gap={4} class="mt-auto p-4">
|
|
256
|
-
<MockImmichStorageSpace
|
|
262
|
+
<MockImmichStorageSpace>
|
|
263
|
+
<ImmichBackupsCard
|
|
264
|
+
{configured}
|
|
265
|
+
lastBackup={configured ? twoHoursAgo : undefined}
|
|
266
|
+
onclick={() => (route = "settings")}
|
|
267
|
+
/>
|
|
268
|
+
</MockImmichStorageSpace>
|
|
257
269
|
|
|
258
270
|
{#if route !== "settings"}
|
|
259
271
|
<MockImmichPurchaseInfo />
|
|
260
272
|
{/if}
|
|
261
273
|
|
|
262
|
-
<
|
|
263
|
-
<span class="bg-success size-2 rounded-full"></span>
|
|
264
|
-
<Text size="small" class="flex-1">Server Online</Text>
|
|
265
|
-
<Text size="small" color="muted">v3.0.3</Text>
|
|
266
|
-
</HStack>
|
|
274
|
+
<MockImmichServerStatus />
|
|
267
275
|
</Stack>
|
|
268
276
|
</div>
|
|
269
277
|
</AppShellSidebar>
|
|
@@ -277,7 +285,13 @@
|
|
|
277
285
|
<ImmichBackupsPage
|
|
278
286
|
price="$1"
|
|
279
287
|
includedStorage="50 GB"
|
|
280
|
-
questions={
|
|
288
|
+
questions={[
|
|
289
|
+
{
|
|
290
|
+
title: "Already back up your library elsewhere?",
|
|
291
|
+
answer: hideReminder,
|
|
292
|
+
},
|
|
293
|
+
...sampleQuestions,
|
|
294
|
+
]}
|
|
281
295
|
/>
|
|
282
296
|
{:else}
|
|
283
297
|
<MockImmichPhotos />
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { Field, Switch } from "@immich/ui";
|
|
3
|
+
|
|
4
|
+
let hidden = $state(false);
|
|
5
|
+
</script>
|
|
6
|
+
|
|
7
|
+
<Field
|
|
8
|
+
label="Hide backups reminder"
|
|
9
|
+
description="If you already manage backups outside of Immich, you can hide the backup setup reminder."
|
|
10
|
+
>
|
|
11
|
+
<Switch bind:checked={hidden} />
|
|
12
|
+
</Field>
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { HStack, Text } from "@immich/ui";
|
|
3
|
+
</script>
|
|
4
|
+
|
|
5
|
+
<HStack gap={2}>
|
|
6
|
+
<span class="bg-success size-2 rounded-full"></span>
|
|
7
|
+
<Text size="small" class="flex-1">Server Online</Text>
|
|
8
|
+
<Text size="small" color="muted">v3.0.3</Text>
|
|
9
|
+
</HStack>
|
|
@@ -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 MockImmichServerStatus: $$__sveltets_2_IsomorphicComponent<Record<string, never>, {
|
|
15
|
+
[evt: string]: CustomEvent<any>;
|
|
16
|
+
}, {}, {}, string>;
|
|
17
|
+
type MockImmichServerStatus = InstanceType<typeof MockImmichServerStatus>;
|
|
18
|
+
export default MockImmichServerStatus;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { Stack } from "@immich/ui";
|
|
3
|
+
import type { Snippet } from "svelte";
|
|
4
|
+
import MockImmichServerStatus from "./MockImmichServerStatus.svelte";
|
|
5
|
+
import MockImmichStorageSpace from "./MockImmichStorageSpace.svelte";
|
|
6
|
+
|
|
7
|
+
type Props = {
|
|
8
|
+
children: Snippet;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const { children }: Props = $props();
|
|
12
|
+
</script>
|
|
13
|
+
|
|
14
|
+
<Stack gap={4} class="bg-light w-80 shrink-0 rounded-2xl border p-4">
|
|
15
|
+
<MockImmichStorageSpace>
|
|
16
|
+
{@render children()}
|
|
17
|
+
</MockImmichStorageSpace>
|
|
18
|
+
|
|
19
|
+
<MockImmichServerStatus />
|
|
20
|
+
</Stack>
|
|
@@ -1,37 +1,25 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { ProgressBar, Stack, Text } from "@immich/ui";
|
|
3
|
+
import type { Snippet } from "svelte";
|
|
4
4
|
|
|
5
5
|
type Props = {
|
|
6
|
-
|
|
6
|
+
children?: Snippet;
|
|
7
7
|
};
|
|
8
8
|
|
|
9
|
-
const {
|
|
9
|
+
const { children }: Props = $props();
|
|
10
10
|
</script>
|
|
11
11
|
|
|
12
|
-
<div class="bg-
|
|
13
|
-
<
|
|
14
|
-
<
|
|
12
|
+
<div class="bg-subtle overflow-hidden rounded-xl border">
|
|
13
|
+
<Stack gap={1} class="p-4 border-b">
|
|
14
|
+
<Text fontWeight="medium">Storage space</Text>
|
|
15
|
+
<Text size="small" color="muted">22.1 GiB of 100 GiB used</Text>
|
|
16
|
+
<ProgressBar
|
|
17
|
+
progress={0.221}
|
|
18
|
+
shape="round"
|
|
15
19
|
size="tiny"
|
|
16
|
-
class="
|
|
17
|
-
containerClass="gap-2 leading-6"
|
|
18
|
-
label="Storage"
|
|
19
|
-
valueLabel="22.1 GiB of 100 GiB used"
|
|
20
|
-
value={0.221}
|
|
20
|
+
class="mt-2 border-none"
|
|
21
21
|
/>
|
|
22
|
-
</
|
|
22
|
+
</Stack>
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
type="button"
|
|
26
|
-
onclick={onBackups}
|
|
27
|
-
class="bg-success-50 text-success-700 flex w-full cursor-pointer items-center gap-2 px-3 py-3 text-start"
|
|
28
|
-
>
|
|
29
|
-
<Icon icon={mdiCloudCheckOutline} size="1em" class="shrink-0" />
|
|
30
|
-
|
|
31
|
-
<Text size="small" color="success" class="flex-1 truncate">
|
|
32
|
-
Backed up 2 hours ago
|
|
33
|
-
</Text>
|
|
34
|
-
|
|
35
|
-
<Icon icon={mdiChevronRight} size="1em" class="shrink-0" />
|
|
36
|
-
</button>
|
|
24
|
+
{@render children?.()}
|
|
37
25
|
</div>
|
|
@@ -8,7 +8,7 @@ export declare const runHistoryKeys: {
|
|
|
8
8
|
export declare const runKeys: {
|
|
9
9
|
byId: (id: string) => readonly ["run", string];
|
|
10
10
|
};
|
|
11
|
-
export declare const useRunHistory: (repositoryId: string) => import("@tanstack/svelte-query").CreateQueryResult<sdk.RunDto[], Error>;
|
|
11
|
+
export declare const useRunHistory: (repositoryId: () => string | undefined) => import("@tanstack/svelte-query").CreateQueryResult<sdk.RunDto[], Error>;
|
|
12
12
|
export declare const useRun: (logId: string) => import("@tanstack/svelte-query").CreateQueryResult<sdk.RunDto, Error>;
|
|
13
13
|
export declare const useRunEventHandler: () => {
|
|
14
14
|
onRunCreate(event: SocketEvent<{
|
|
@@ -13,8 +13,9 @@ export const runKeys = {
|
|
|
13
13
|
byId: (id) => ['run', id],
|
|
14
14
|
};
|
|
15
15
|
export const useRunHistory = (repositoryId) => createQuery(() => ({
|
|
16
|
-
queryKey: runHistoryKeys.byRepository(repositoryId),
|
|
17
|
-
queryFn: () => getRunHistory(repositoryId).then(({ runs }) => runs.toSorted((a, b) => b.start.localeCompare(a.start))),
|
|
16
|
+
queryKey: runHistoryKeys.byRepository(repositoryId() ?? ''),
|
|
17
|
+
queryFn: () => getRunHistory(repositoryId()).then(({ runs }) => runs.toSorted((a, b) => b.start.localeCompare(a.start))),
|
|
18
|
+
enabled: repositoryId() !== undefined,
|
|
18
19
|
}), () => queryClient);
|
|
19
20
|
export const useRun = (logId) => createQuery(() => ({
|
|
20
21
|
queryKey: runKeys.byId(logId),
|
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.
|
|
5
|
+
"version": "0.30.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.
|
|
67
|
+
"@futo-org/backups-api-client": "^0.30.1"
|
|
68
68
|
},
|
|
69
69
|
"scripts": {
|
|
70
70
|
"dev": "vite dev --port 5174",
|