Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/yucca-sdk/orchestration-ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@
"@immich/ui": "^0.59.0",
"@mdi/js": "^7.4.47",
"@oazapfts/runtime": "^1.1.0",
"@tanstack/svelte-query": "^6.1.3",
"cron-validate": "^1.5.3",
"d3": "^7.9.0",
"lodash.debounce": "^4.0.8",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
<script lang="ts">
import { onMount } from "svelte";
import { defaults, getBackends, type BackendDto } from "../../fetch-client";
import {
Alert,
Button,
Card,
CardBody,
Expand All @@ -11,31 +10,23 @@
LoadingSpinner,
Text,
} from "@immich/ui";
import { getReadableErrorMessage } from "$lib/utils/handle-error";
import { options } from "$lib/options";

// svelte-ignore state_referenced_locally
let backends: BackendDto[] | undefined = $state();
import { handleYuccaLogin, useBackends } from "$lib/services/backend.service";

const { advanced } = options;

onMount(() => {
if (!backends) {
getBackends().then((data) => (backends = data.backends));
}
});
const query = useBackends();

const yuccaBackend = $derived(
backends?.find((backend) => backend.type === "yucca"),
query.data?.find((backend) => backend.type === "yucca"),
);

const login = () => {
const loginUrl = new URL("/api/auth/oidc/login", defaults.baseUrl);
loginUrl.searchParams.set("next", window.location.href);
window.location.href = loginUrl.href;
};
</script>

{#if backends}
{#if query.isLoading}
<LoadingSpinner />
{:else if query.isError}
<Alert color="danger">{getReadableErrorMessage(query.error)}</Alert>
{:else if query.isSuccess}
{#if yuccaBackend}
<Card color={yuccaBackend.isOnline ? "success" : "danger"}>
<CardBody class="flex flex-col gap-2">
Expand All @@ -48,7 +39,7 @@
</CardFooter>
{:else}
<CardFooter>
<Button onclick={login} size="small">Login again</Button>
<Button onclick={handleYuccaLogin} size="small">Login again</Button>
</CardFooter>
{/if}
</Card>
Expand All @@ -59,12 +50,12 @@
<Text>Upsell text here</Text>
</CardBody>
<CardFooter>
<Button onclick={login} size="small">Get started</Button>
<Button onclick={handleYuccaLogin} size="small">Get started</Button>
</CardFooter>
</Card>
{/if}

{#each backends as backend (backend.id)}
{#each query.data as backend (backend.id)}
{#if backend.type !== "yucca"}
<Card color={backend.isOnline ? "success" : "danger"}>
<CardBody class="flex flex-col gap-2">
Expand All @@ -85,6 +76,4 @@
<Button size="small">Setup new S3 storage (🚧)</Button>
</HStack>
{/if}
{:else}
<LoadingSpinner />
{/if}
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
<script lang="ts">
import type {
LocalRepositoryDto,
RepositoryListResponseDto,
} from "$lib/fetch-client";
import { getProvider } from "$lib/providers";
import { Button, Heading, modalManager } from "@immich/ui";
import { onMount } from "svelte";
import type { RepositoryListResponseDto } from "$lib/fetch-client";
import { Alert, Button, Heading, LoadingSpinner, modalManager } from "@immich/ui";
import { getReadableErrorMessage } from "$lib/utils/handle-error";
import BackupItem from "./BackupItem.svelte";
import CreateRepositoryModal from "./dialogs/CreateRepositoryModal.svelte";
import OnEvents from "../util/OnEvents.svelte";
import { SocketEvent } from "$lib/events";
import {
useRepositories,
useRepositoryEventHandler,
} from "$lib/services/repository.service";

interface Props {
local?: boolean;
Expand All @@ -19,88 +18,58 @@
const { local, initialData }: Props = $props();

// svelte-ignore state_referenced_locally
let repositories = $state(initialData?.repositories);
const query = useRepositories(initialData?.repositories);

const provider = getProvider();

onMount(() => {
if (!repositories) {
provider
.getRepositories()
.then((data) => (repositories = data.repositories));
}
});
const { onRepositoryCreate, onRepositoryUpdate } =
useRepositoryEventHandler();

const localRepositories = $derived(
repositories?.filter((repository) => repository.configuration) ?? [],
query.data?.filter((repository) => repository.configuration) ?? [],
);

const remoteRepositories = $derived(
repositories?.filter((repository) => !repository.configuration) ?? [],
query.data?.filter((repository) => !repository.configuration) ?? [],
);

const createNewBackup = () => modalManager.show(CreateRepositoryModal);

const onRepositoryCreate = (
event: SocketEvent<{
repository: LocalRepositoryDto;
}>,
) => {
repositories = [
...(repositories ?? []).filter(
(repository) => repository.id !== event.data.repository.id,
),
event.data.repository,
];
};

const onRepositoryUpdate = (
event: SocketEvent<{
repositoryId: string;
repository: Partial<LocalRepositoryDto>;
}>,
) => {
repositories = repositories?.map((repository) =>
repository.id === event.data.repositoryId
? {
...repository,
...event.data.repository,
}
: repository,
);
};
</script>

<OnEvents {onRepositoryCreate} {onRepositoryUpdate} />

<div class="flex flex-col gap-4">
{#if local}
{#if query.isLoading}
<LoadingSpinner />
{:else if query.isError}
<Alert color="danger">{getReadableErrorMessage(query.error)}</Alert>
{:else if query.isSuccess}
<div class="flex flex-col gap-4">
{#if local}
<div class="flex flex-col gap-2">
<Heading
>Backups on this machine <div class="inline-block">
<Button
shape="round"
size="tiny"
variant="outline"
onclick={createNewBackup}>Create new backup</Button
>
</div></Heading
>
{#each localRepositories as repository (repository.id)}
<BackupItem {repository} />
{/each}
</div>
{/if}

<div class="flex flex-col gap-2">
<Heading
>Backups on this machine <div class="inline-block">
<Button
shape="round"
size="tiny"
variant="outline"
onclick={createNewBackup}>Create new backup</Button
>
</div></Heading
>
{#each localRepositories as repository (repository.id)}
{#if local}
<Heading>Backups found elsewhere</Heading>
{:else}
<Heading>Your Backups</Heading>
{/if}

{#each remoteRepositories as repository (repository.id)}
<BackupItem {repository} />
{/each}
</div>
{/if}

<div class="flex flex-col gap-2">
{#if local}
<Heading>Backups found elsewhere</Heading>
{:else}
<Heading>Your Backups</Heading>
{/if}

{#each remoteRepositories as repository (repository.id)}
<BackupItem {repository} />
{/each}
</div>
</div>
{/if}
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@
import { type LocalRepositoryDto } from "$lib/fetch-client";
import { mdiClose } from "@mdi/js";
import FileBrowserModal from "./FileBrowserModal.svelte";
import OnEvents from "$lib/components/util/OnEvents.svelte";
import type { SocketEvent } from "$lib/events";
import { handleUpdateRepository } from "$lib/services/repository.service";
import { SvelteSet } from "svelte/reactivity";

Expand All @@ -30,29 +28,6 @@
// svelte-ignore state_referenced_locally
let paths = new SvelteSet(repository.configuration?.paths ?? []);

const onRepositoryUpdate = (
event: SocketEvent<{
repositoryId: string;
repository: Partial<LocalRepositoryDto>;
}>,
) => {
const { repository, repositoryId } = event.data;

if (repositoryId === repository.id) {
if (repository.name) {
name = repository.name;
}

if (repository.configuration) {
paths.clear();

for (const path of repository.configuration!.paths) {
paths.add(path);
}
}
}
};

const onSubmit = async () => {
await handleUpdateRepository(
repository.id,
Expand All @@ -64,8 +39,6 @@
};
</script>

<OnEvents {onRepositoryUpdate} />

<FormModal
disabled={name.length === 0}
title={`Configure ${name}`}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<script lang="ts">
import {
Alert,
Button,
LoadingSpinner,
Modal,
Expand All @@ -12,10 +13,10 @@
TableHeading,
TableRow,
} from "@immich/ui";
import type { LocalRepositoryDto, RunDto } from "$lib/fetch-client";
import { onMount } from "svelte";
import { getReadableErrorMessage } from "$lib/utils/handle-error";
import type { LocalRepositoryDto } from "$lib/fetch-client";
import ViewLogModal from "./ViewLogModal.svelte";
import { handleGetRunHistory } from "$lib/services/runHistory.service";
import { useRunHistory } from "$lib/services/runHistory.service";

interface Props {
repository: LocalRepositoryDto;
Expand All @@ -24,17 +25,17 @@

let { repository, onClose }: Props = $props();

let runs: RunDto[] | undefined = $state();

onMount(async () => {
const result = await handleGetRunHistory(repository.id);
runs = result.runs.toSorted((a, b) => b.start.localeCompare(a.start));
});
// svelte-ignore state_referenced_locally
const query = useRunHistory(repository.id);
</script>

<Modal title={`Run History for ${repository.name}`} size="giant" {onClose}>
<ModalBody>
{#if runs}
{#if query.isLoading}
<LoadingSpinner />
{:else if query.isError}
<Alert color="danger">{getReadableErrorMessage(query.error)}</Alert>
{:else if query.isSuccess}
<Table>
<TableHeader>
<TableHeading>Start</TableHeading>
Expand All @@ -44,7 +45,7 @@
</TableHeader>

<TableBody>
{#each runs as run (run.id)}
{#each query.data as run (run.id)}
<TableRow>
<TableCell>{run.start}</TableCell>
<TableCell>{run.end}</TableCell>
Expand All @@ -61,8 +62,6 @@
{/each}
</TableBody>
</Table>
{:else}
<LoadingSpinner />
{/if}
</ModalBody>
</Modal>
Loading
Loading