@futo-org/backups-orchestrator-ui 0.1.71
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/LICENSE +41 -0
- package/dist/components/backends/BackendItem.svelte +64 -0
- package/dist/components/backends/BackendItem.svelte.d.ts +10 -0
- package/dist/components/backends/BackendsList.svelte +73 -0
- package/dist/components/backends/BackendsList.svelte.d.ts +7 -0
- package/dist/components/backends/CreateLocalBackend.svelte +46 -0
- package/dist/components/backends/CreateLocalBackend.svelte.d.ts +7 -0
- package/dist/components/backends/OAuthDeviceFlow.svelte +78 -0
- package/dist/components/backends/OAuthDeviceFlow.svelte.d.ts +9 -0
- package/dist/components/backups/BackupItem.svelte +59 -0
- package/dist/components/backups/BackupItem.svelte.d.ts +7 -0
- package/dist/components/backups/BackupsList.svelte +82 -0
- package/dist/components/backups/BackupsList.svelte.d.ts +8 -0
- package/dist/components/backups/dialogs/ConfigureRepositoryModal.svelte +102 -0
- package/dist/components/backups/dialogs/ConfigureRepositoryModal.svelte.d.ts +8 -0
- package/dist/components/backups/dialogs/CreateRepositoryModal.svelte +59 -0
- package/dist/components/backups/dialogs/CreateRepositoryModal.svelte.d.ts +6 -0
- package/dist/components/backups/dialogs/ImportRepositoryModal.svelte +62 -0
- package/dist/components/backups/dialogs/ImportRepositoryModal.svelte.d.ts +8 -0
- package/dist/components/backups/dialogs/RestoreSnapshotModal.svelte +109 -0
- package/dist/components/backups/dialogs/RestoreSnapshotModal.svelte.d.ts +8 -0
- package/dist/components/backups/dialogs/ViewLogModal.svelte +208 -0
- package/dist/components/backups/dialogs/ViewLogModal.svelte.d.ts +7 -0
- package/dist/components/backups/metrics-history/MetricsHistoryModal.svelte +166 -0
- package/dist/components/backups/metrics-history/MetricsHistoryModal.svelte.d.ts +8 -0
- package/dist/components/backups/run-history/RepositoryRunHistory.svelte +39 -0
- package/dist/components/backups/run-history/RepositoryRunHistory.svelte.d.ts +7 -0
- package/dist/components/backups/run-history/RepositoryRunHistoryItem.svelte +45 -0
- package/dist/components/backups/run-history/RepositoryRunHistoryItem.svelte.d.ts +7 -0
- package/dist/components/backups/run-history/RunHistoryModal.svelte +18 -0
- package/dist/components/backups/run-history/RunHistoryModal.svelte.d.ts +8 -0
- package/dist/components/backups/snapshots-list/RepositorySnapshotsList.svelte +53 -0
- package/dist/components/backups/snapshots-list/RepositorySnapshotsList.svelte.d.ts +7 -0
- package/dist/components/backups/snapshots-list/RepositorySnapshotsListItem.svelte +41 -0
- package/dist/components/backups/snapshots-list/RepositorySnapshotsListItem.svelte.d.ts +8 -0
- package/dist/components/backups/snapshots-list/SnapshotsListModal.svelte +18 -0
- package/dist/components/backups/snapshots-list/SnapshotsListModal.svelte.d.ts +8 -0
- package/dist/components/dashboard/Dashboard.svelte +52 -0
- package/dist/components/dashboard/Dashboard.svelte.d.ts +9 -0
- package/dist/components/dashboard/DashboardAvgBackupTime.svelte +34 -0
- package/dist/components/dashboard/DashboardAvgBackupTime.svelte.d.ts +7 -0
- package/dist/components/dashboard/DashboardBackupHealth.svelte +91 -0
- package/dist/components/dashboard/DashboardBackupHealth.svelte.d.ts +9 -0
- package/dist/components/dashboard/DashboardCurrentUsage.svelte +10 -0
- package/dist/components/dashboard/DashboardCurrentUsage.svelte.d.ts +18 -0
- package/dist/components/dashboard/DashboardDailyBackupTime.svelte +31 -0
- package/dist/components/dashboard/DashboardDailyBackupTime.svelte.d.ts +7 -0
- package/dist/components/dashboard/DashboardInstall.svelte +15 -0
- package/dist/components/dashboard/DashboardInstall.svelte.d.ts +18 -0
- package/dist/components/dashboard/DashboardRecentBackups.svelte +104 -0
- package/dist/components/dashboard/DashboardRecentBackups.svelte.d.ts +8 -0
- package/dist/components/dashboard/DashboardTotalStored.svelte +27 -0
- package/dist/components/dashboard/DashboardTotalStored.svelte.d.ts +7 -0
- package/dist/components/integrations/immich/ImmichBackupsPage.svelte +14 -0
- package/dist/components/integrations/immich/ImmichBackupsPage.svelte.d.ts +6 -0
- package/dist/components/integrations/immich/ImmichConfigureBackup.svelte +402 -0
- package/dist/components/integrations/immich/ImmichConfigureBackup.svelte.d.ts +9 -0
- package/dist/components/integrations/immich/ImmichConfirmDefaultBackup.svelte +80 -0
- package/dist/components/integrations/immich/ImmichConfirmDefaultBackup.svelte.d.ts +8 -0
- package/dist/components/integrations/immich/ImmichManageBackup.svelte +77 -0
- package/dist/components/integrations/immich/ImmichManageBackup.svelte.d.ts +3 -0
- package/dist/components/integrations/immich/ImmichManageBackupOverview.svelte +100 -0
- package/dist/components/integrations/immich/ImmichManageBackupOverview.svelte.d.ts +8 -0
- package/dist/components/integrations/immich/ImmichOnboardingRestoreFlow.svelte +75 -0
- package/dist/components/integrations/immich/ImmichOnboardingRestoreFlow.svelte.d.ts +7 -0
- package/dist/components/integrations/immich/ImmichOnboardingSetupFlow.svelte +113 -0
- package/dist/components/integrations/immich/ImmichOnboardingSetupFlow.svelte.d.ts +8 -0
- package/dist/components/onboarding/OnboardingGate.svelte +48 -0
- package/dist/components/onboarding/OnboardingGate.svelte.d.ts +9 -0
- package/dist/components/onboarding/RecoveryKeyDisplay.svelte +103 -0
- package/dist/components/onboarding/RecoveryKeyDisplay.svelte.d.ts +6 -0
- package/dist/components/onboarding/SampleOnboarding.svelte +98 -0
- package/dist/components/onboarding/SampleOnboarding.svelte.d.ts +9 -0
- package/dist/components/onboarding/dialogs/BackupsRecoveryKeyModal.svelte +43 -0
- package/dist/components/onboarding/dialogs/BackupsRecoveryKeyModal.svelte.d.ts +6 -0
- package/dist/components/onboarding/restore-point-flow/RestorePointFlow.svelte +96 -0
- package/dist/components/onboarding/restore-point-flow/RestorePointFlow.svelte.d.ts +8 -0
- package/dist/components/onboarding/restore-point-flow/RestorePointFlow2SelectSnapshot.svelte +83 -0
- package/dist/components/onboarding/restore-point-flow/RestorePointFlow2SelectSnapshot.svelte.d.ts +9 -0
- package/dist/components/onboarding/restore-point-flow/RestorePointFlow3ConfirmRestore.svelte +118 -0
- package/dist/components/onboarding/restore-point-flow/RestorePointFlow3ConfirmRestore.svelte.d.ts +10 -0
- package/dist/components/onboarding/restore-point-flow/RestorePointFlow4Restore.svelte +59 -0
- package/dist/components/onboarding/restore-point-flow/RestorePointFlow4Restore.svelte.d.ts +8 -0
- package/dist/components/onboarding/stages/OnboardingStageBackupServices.svelte +82 -0
- package/dist/components/onboarding/stages/OnboardingStageBackupServices.svelte.d.ts +8 -0
- package/dist/components/onboarding/stages/OnboardingStageKeyConfirm.svelte +56 -0
- package/dist/components/onboarding/stages/OnboardingStageKeyConfirm.svelte.d.ts +9 -0
- package/dist/components/onboarding/stages/OnboardingStageKeyImport.svelte +57 -0
- package/dist/components/onboarding/stages/OnboardingStageKeyImport.svelte.d.ts +8 -0
- package/dist/components/onboarding/stages/OnboardingStageKeyIntro.svelte +50 -0
- package/dist/components/onboarding/stages/OnboardingStageKeyIntro.svelte.d.ts +7 -0
- package/dist/components/onboarding/stages/OnboardingStageKeySave.svelte +44 -0
- package/dist/components/onboarding/stages/OnboardingStageKeySave.svelte.d.ts +8 -0
- package/dist/components/onboarding/stages/OnboardingStageWelcome.svelte +56 -0
- package/dist/components/onboarding/stages/OnboardingStageWelcome.svelte.d.ts +9 -0
- package/dist/components/onboarding/stages/SampleCreateFirstBackup.svelte +43 -0
- package/dist/components/onboarding/stages/SampleCreateFirstBackup.svelte.d.ts +7 -0
- package/dist/components/onboarding/stages/SampleCreateFirstSchedule.svelte +49 -0
- package/dist/components/onboarding/stages/SampleCreateFirstSchedule.svelte.d.ts +7 -0
- package/dist/components/schedules/RepositoryPicker.svelte +105 -0
- package/dist/components/schedules/RepositoryPicker.svelte.d.ts +6 -0
- package/dist/components/schedules/ScheduleItem.svelte +47 -0
- package/dist/components/schedules/ScheduleItem.svelte.d.ts +8 -0
- package/dist/components/schedules/ScheduleList.svelte +51 -0
- package/dist/components/schedules/ScheduleList.svelte.d.ts +3 -0
- package/dist/components/schedules/dialogs/ConfigureScheduleModal.svelte +48 -0
- package/dist/components/schedules/dialogs/ConfigureScheduleModal.svelte.d.ts +8 -0
- package/dist/components/schedules/dialogs/CreateScheduleModal.svelte +43 -0
- package/dist/components/schedules/dialogs/CreateScheduleModal.svelte.d.ts +6 -0
- package/dist/components/test/ImmichTestUi.svelte +183 -0
- package/dist/components/test/ImmichTestUi.svelte.d.ts +6 -0
- package/dist/components/test/TestUi.svelte +134 -0
- package/dist/components/test/TestUi.svelte.d.ts +6 -0
- package/dist/components/test/dashboard/ActiveJobs.svelte +380 -0
- package/dist/components/test/dashboard/ActiveJobs.svelte.d.ts +3 -0
- package/dist/components/test/dashboard/BackupHealth.svelte +95 -0
- package/dist/components/test/dashboard/BackupHealth.svelte.d.ts +7 -0
- package/dist/components/test/dashboard/BackupStats.svelte +117 -0
- package/dist/components/test/dashboard/BackupStats.svelte.d.ts +8 -0
- package/dist/components/test/dashboard/Dashboard.svelte +76 -0
- package/dist/components/test/dashboard/Dashboard.svelte.d.ts +6 -0
- package/dist/components/test/dashboard/RecentBackups.svelte +96 -0
- package/dist/components/test/dashboard/RecentBackups.svelte.d.ts +8 -0
- package/dist/components/ui/PageLayout.svelte +67 -0
- package/dist/components/ui/PageLayout.svelte.d.ts +10 -0
- package/dist/components/ui/PathListField.svelte +83 -0
- package/dist/components/ui/PathListField.svelte.d.ts +17 -0
- package/dist/components/ui/PathPickerField.svelte +74 -0
- package/dist/components/ui/PathPickerField.svelte.d.ts +15 -0
- package/dist/components/ui/PathPickerModal.svelte +219 -0
- package/dist/components/ui/PathPickerModal.svelte.d.ts +14 -0
- package/dist/components/ui/StackList.svelte +30 -0
- package/dist/components/ui/StackList.svelte.d.ts +30 -0
- package/dist/components/ui/StackListItem.svelte +64 -0
- package/dist/components/ui/StackListItem.svelte.d.ts +13 -0
- package/dist/components/ui/VisualisationGauge.svelte +25 -0
- package/dist/components/ui/VisualisationGauge.svelte.d.ts +10 -0
- package/dist/components/ui/VisualisationSegmentedBar.svelte +48 -0
- package/dist/components/ui/VisualisationSegmentedBar.svelte.d.ts +14 -0
- package/dist/components/util/OnEvents.svelte +31 -0
- package/dist/components/util/OnEvents.svelte.d.ts +7 -0
- package/dist/components/util/RelativeTime.svelte +21 -0
- package/dist/components/util/RelativeTime.svelte.d.ts +6 -0
- package/dist/components/util/Suspense.svelte +21 -0
- package/dist/components/util/Suspense.svelte.d.ts +29 -0
- package/dist/components/util/TimedButton.svelte +37 -0
- package/dist/components/util/TimedButton.svelte.d.ts +7 -0
- package/dist/components/util/YuccaContext.svelte +26 -0
- package/dist/components/util/YuccaContext.svelte.d.ts +8 -0
- package/dist/events.d.ts +6 -0
- package/dist/events.js +47 -0
- package/dist/fetch-client.d.ts +289 -0
- package/dist/fetch-client.js +233 -0
- package/dist/index.d.ts +28 -0
- package/dist/index.js +28 -0
- package/dist/options.d.ts +5 -0
- package/dist/options.js +6 -0
- package/dist/providers.d.ts +11 -0
- package/dist/providers.js +35 -0
- package/dist/query-client.d.ts +2 -0
- package/dist/query-client.js +2 -0
- package/dist/services/backend.service.d.ts +18 -0
- package/dist/services/backend.service.js +61 -0
- package/dist/services/filesystem.service.d.ts +2 -0
- package/dist/services/filesystem.service.js +11 -0
- package/dist/services/immich.integration.service.d.ts +6 -0
- package/dist/services/immich.integration.service.js +24 -0
- package/dist/services/integrations.service.d.ts +13 -0
- package/dist/services/integrations.service.js +42 -0
- package/dist/services/log.service.svelte.d.ts +53 -0
- package/dist/services/log.service.svelte.js +93 -0
- package/dist/services/metricsHistory.service.d.ts +4 -0
- package/dist/services/metricsHistory.service.js +12 -0
- package/dist/services/onboarding.service.d.ts +11 -0
- package/dist/services/onboarding.service.js +56 -0
- package/dist/services/repository.service.d.ts +45 -0
- package/dist/services/repository.service.js +157 -0
- package/dist/services/runHistory.service.d.ts +26 -0
- package/dist/services/runHistory.service.js +54 -0
- package/dist/services/schedule.service.d.ts +35 -0
- package/dist/services/schedule.service.js +126 -0
- package/dist/services/snapshot.service.d.ts +29 -0
- package/dist/services/snapshot.service.js +108 -0
- package/dist/services/task.service.d.ts +3 -0
- package/dist/services/task.service.js +20 -0
- package/dist/utils/actions.d.ts +2 -0
- package/dist/utils/actions.js +3 -0
- package/dist/utils/format.d.ts +2 -0
- package/dist/utils/format.js +24 -0
- package/dist/utils/handle-error.d.ts +9 -0
- package/dist/utils/handle-error.js +42 -0
- package/package.json +79 -0
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { type LocalRepositoryDto } from "../../../fetch-client";
|
|
3
|
+
import {
|
|
4
|
+
handleCheckImportRepository,
|
|
5
|
+
useImportRepository,
|
|
6
|
+
} from "../../../services/repository.service";
|
|
7
|
+
import { FormModal, LoadingSpinner, modalManager, Text } from "@immich/ui";
|
|
8
|
+
import { onMount } from "svelte";
|
|
9
|
+
import ConfigureRepositoryModal from "./ConfigureRepositoryModal.svelte";
|
|
10
|
+
|
|
11
|
+
type Props = {
|
|
12
|
+
onClose: () => void;
|
|
13
|
+
repository: LocalRepositoryDto;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
let { onClose, repository }: Props = $props();
|
|
17
|
+
let readable: boolean | undefined = $state();
|
|
18
|
+
|
|
19
|
+
onMount(async () => {
|
|
20
|
+
const check = await handleCheckImportRepository(
|
|
21
|
+
repository.id,
|
|
22
|
+
repository.backends!.primary.id,
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
readable = check.readable;
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
const mutation = useImportRepository();
|
|
29
|
+
|
|
30
|
+
const onSubmit = () =>
|
|
31
|
+
mutation.mutate(
|
|
32
|
+
{ id: repository.id, backendId: repository.backends!.primary.id },
|
|
33
|
+
{
|
|
34
|
+
onSuccess: ({ repository: created }) => {
|
|
35
|
+
onClose();
|
|
36
|
+
|
|
37
|
+
modalManager.open(ConfigureRepositoryModal, {
|
|
38
|
+
repository: {
|
|
39
|
+
...created,
|
|
40
|
+
configuration: created.configuration!,
|
|
41
|
+
},
|
|
42
|
+
});
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
);
|
|
46
|
+
</script>
|
|
47
|
+
|
|
48
|
+
<FormModal
|
|
49
|
+
title={`Import ${repository.name}`}
|
|
50
|
+
submitText="Import"
|
|
51
|
+
disabled={readable !== true || mutation.isPending}
|
|
52
|
+
{onSubmit}
|
|
53
|
+
{onClose}
|
|
54
|
+
>
|
|
55
|
+
{#if readable === undefined}
|
|
56
|
+
<LoadingSpinner />
|
|
57
|
+
{:else if readable}
|
|
58
|
+
<Text>Repository is readable and accessible!</Text>
|
|
59
|
+
{:else}
|
|
60
|
+
<Text>Can't read repository.</Text>
|
|
61
|
+
{/if}
|
|
62
|
+
</FormModal>
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type LocalRepositoryDto } from "../../../fetch-client";
|
|
2
|
+
type Props = {
|
|
3
|
+
onClose: () => void;
|
|
4
|
+
repository: LocalRepositoryDto;
|
|
5
|
+
};
|
|
6
|
+
declare const ImportRepositoryModal: import("svelte").Component<Props, {}, "">;
|
|
7
|
+
type ImportRepositoryModal = ReturnType<typeof ImportRepositoryModal>;
|
|
8
|
+
export default ImportRepositoryModal;
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import PathListField from "../../ui/PathListField.svelte";
|
|
3
|
+
import PathPickerField from "../../ui/PathPickerField.svelte";
|
|
4
|
+
import type { RepositorySnapshotRestoreRequestDto } from "../../../fetch-client";
|
|
5
|
+
import {
|
|
6
|
+
handleGetSnapshotListing,
|
|
7
|
+
useRestoreSnapshot,
|
|
8
|
+
} from "../../../services/snapshot.service";
|
|
9
|
+
import {
|
|
10
|
+
FormModal,
|
|
11
|
+
Heading,
|
|
12
|
+
HStack,
|
|
13
|
+
modalManager,
|
|
14
|
+
Stack,
|
|
15
|
+
Switch,
|
|
16
|
+
Text,
|
|
17
|
+
} from "@immich/ui";
|
|
18
|
+
import { SvelteSet } from "svelte/reactivity";
|
|
19
|
+
import ViewLogModal from "./ViewLogModal.svelte";
|
|
20
|
+
|
|
21
|
+
type Props = {
|
|
22
|
+
repository: string;
|
|
23
|
+
snapshot: string;
|
|
24
|
+
onClose: () => void;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
let { repository, snapshot, onClose }: Props = $props();
|
|
28
|
+
|
|
29
|
+
let inPlace = $state(true);
|
|
30
|
+
let target = $state("");
|
|
31
|
+
let include = new SvelteSet<string>();
|
|
32
|
+
|
|
33
|
+
const mutation = useRestoreSnapshot();
|
|
34
|
+
|
|
35
|
+
const onSubmit = () => {
|
|
36
|
+
const dto: RepositorySnapshotRestoreRequestDto = {
|
|
37
|
+
include: [...include],
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
if (!inPlace) {
|
|
41
|
+
dto.target = target;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
mutation.mutate(
|
|
45
|
+
{ repositoryId: repository, snapshotId: snapshot, options: dto },
|
|
46
|
+
{
|
|
47
|
+
onSuccess: ({ logId }) => {
|
|
48
|
+
onClose();
|
|
49
|
+
|
|
50
|
+
modalManager.open(ViewLogModal, {
|
|
51
|
+
logId,
|
|
52
|
+
});
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
);
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
</script>
|
|
59
|
+
|
|
60
|
+
<FormModal
|
|
61
|
+
title="Restore Backup"
|
|
62
|
+
submitText="Restore"
|
|
63
|
+
disabled={mutation.isPending}
|
|
64
|
+
{onSubmit}
|
|
65
|
+
{onClose}
|
|
66
|
+
>
|
|
67
|
+
<Stack gap={5}>
|
|
68
|
+
<PathListField
|
|
69
|
+
paths={include}
|
|
70
|
+
addLabel="Add more files"
|
|
71
|
+
manageLabel="Select files instead"
|
|
72
|
+
pickerTitle="Files to restore"
|
|
73
|
+
pickerDescription="Pick the files and folders to restore. Leave empty to restore everything."
|
|
74
|
+
handleGetListing={(path) =>
|
|
75
|
+
handleGetSnapshotListing(repository, snapshot, path)}
|
|
76
|
+
>
|
|
77
|
+
{#snippet label()}Files to restore{/snippet}
|
|
78
|
+
{#snippet empty()}Restoring all files and folders.{/snippet}
|
|
79
|
+
</PathListField>
|
|
80
|
+
|
|
81
|
+
<Stack gap={4}>
|
|
82
|
+
<Heading class="px-1" size="tiny">Options</Heading>
|
|
83
|
+
|
|
84
|
+
<HStack gap={4}>
|
|
85
|
+
<Stack gap={0}>
|
|
86
|
+
<Text>In-place restore</Text>
|
|
87
|
+
<Text color="secondary" size="small">
|
|
88
|
+
Restore files to where they were originally.
|
|
89
|
+
</Text>
|
|
90
|
+
</Stack>
|
|
91
|
+
<Switch bind:checked={inPlace} />
|
|
92
|
+
</HStack>
|
|
93
|
+
|
|
94
|
+
{#if !inPlace}
|
|
95
|
+
<PathPickerField
|
|
96
|
+
bind:value={target}
|
|
97
|
+
placeholder="/path/to/restore/into"
|
|
98
|
+
pickerTitle="Choose target folder"
|
|
99
|
+
pickerDescription="Pick the folder to restore files into."
|
|
100
|
+
>
|
|
101
|
+
{#snippet title()}Target{/snippet}
|
|
102
|
+
{#snippet description()}
|
|
103
|
+
Where do you want this backup restored to?
|
|
104
|
+
{/snippet}
|
|
105
|
+
</PathPickerField>
|
|
106
|
+
{/if}
|
|
107
|
+
</Stack>
|
|
108
|
+
</Stack>
|
|
109
|
+
</FormModal>
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
type Props = {
|
|
2
|
+
repository: string;
|
|
3
|
+
snapshot: string;
|
|
4
|
+
onClose: () => void;
|
|
5
|
+
};
|
|
6
|
+
declare const RestoreSnapshotModal: import("svelte").Component<Props, {}, "">;
|
|
7
|
+
type RestoreSnapshotModal = ReturnType<typeof RestoreSnapshotModal>;
|
|
8
|
+
export default RestoreSnapshotModal;
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import OnEvents from "../../util/OnEvents.svelte";
|
|
3
|
+
import RelativeTime from "../../util/RelativeTime.svelte";
|
|
4
|
+
import { options } from "../../../options";
|
|
5
|
+
import { createLogObserver } from "../../../services/log.service.svelte";
|
|
6
|
+
import { useRun, useRunEventHandler } from "../../../services/runHistory.service";
|
|
7
|
+
import { formatDuration } from "../../../utils/format";
|
|
8
|
+
import {
|
|
9
|
+
Alert,
|
|
10
|
+
FormatBytes,
|
|
11
|
+
Heading,
|
|
12
|
+
Modal,
|
|
13
|
+
ModalBody,
|
|
14
|
+
ProgressBar,
|
|
15
|
+
Scrollable,
|
|
16
|
+
Stack,
|
|
17
|
+
Text,
|
|
18
|
+
} from "@immich/ui";
|
|
19
|
+
import { DateTime } from "luxon";
|
|
20
|
+
import { onDestroy } from "svelte";
|
|
21
|
+
|
|
22
|
+
type Props = {
|
|
23
|
+
logId: string;
|
|
24
|
+
onClose: () => void;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
let { logId, onClose }: Props = $props();
|
|
28
|
+
|
|
29
|
+
const advanced = options.advanced;
|
|
30
|
+
// svelte-ignore state_referenced_locally
|
|
31
|
+
const log = createLogObserver(logId);
|
|
32
|
+
// svelte-ignore state_referenced_locally
|
|
33
|
+
const runQuery = useRun(logId);
|
|
34
|
+
const run = $derived(runQuery.data);
|
|
35
|
+
const { onRunUpdate } = useRunEventHandler();
|
|
36
|
+
|
|
37
|
+
let now = $state(DateTime.now());
|
|
38
|
+
const tick = setInterval(() => (now = DateTime.now()), 1000);
|
|
39
|
+
|
|
40
|
+
onDestroy(() => {
|
|
41
|
+
clearInterval(tick);
|
|
42
|
+
log.destroy();
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
const running = $derived(run?.status === "incomplete");
|
|
46
|
+
const duration = $derived(
|
|
47
|
+
run
|
|
48
|
+
? formatDuration(
|
|
49
|
+
(run.end ? DateTime.fromISO(run.end) : now).toMillis() -
|
|
50
|
+
DateTime.fromISO(run.start).toMillis(),
|
|
51
|
+
)
|
|
52
|
+
: "",
|
|
53
|
+
);
|
|
54
|
+
</script>
|
|
55
|
+
|
|
56
|
+
<OnEvents {onRunUpdate} />
|
|
57
|
+
|
|
58
|
+
<Modal
|
|
59
|
+
title={run?.type === "restore"
|
|
60
|
+
? "Restore Log"
|
|
61
|
+
: run?.type === "forget"
|
|
62
|
+
? "Prune Log"
|
|
63
|
+
: "Backup Log"}
|
|
64
|
+
size="giant"
|
|
65
|
+
{onClose}
|
|
66
|
+
>
|
|
67
|
+
<ModalBody>
|
|
68
|
+
<Stack gap={5}>
|
|
69
|
+
<Stack gap={1}>
|
|
70
|
+
{#if !run}
|
|
71
|
+
<Heading size="medium">Connecting…</Heading>
|
|
72
|
+
{:else if run.status === "incomplete"}
|
|
73
|
+
<Heading size="medium">
|
|
74
|
+
{#if run.type === "restore"}Restoring{:else if run.type === "forget"}Pruning{:else}Backing
|
|
75
|
+
up{/if} · {Math.round(log.status.progress * 100)}%
|
|
76
|
+
</Heading>
|
|
77
|
+
{:else if run.status === "failed"}
|
|
78
|
+
<Heading size="medium" color="danger">
|
|
79
|
+
{#if run.type === "restore"}Restore{:else if run.type === "forget"}Prune{:else}Backup{/if}
|
|
80
|
+
failed after {duration}
|
|
81
|
+
</Heading>
|
|
82
|
+
{:else if log.errors.length > 0}
|
|
83
|
+
<Heading size="medium" color="warning">
|
|
84
|
+
{#if run.type === "restore"}Restored{:else if run.type === "forget"}Pruned{:else}Backed
|
|
85
|
+
up{/if} in {duration} · {log.errors.length} error{log
|
|
86
|
+
.errors.length === 1
|
|
87
|
+
? ""
|
|
88
|
+
: "s"}
|
|
89
|
+
</Heading>
|
|
90
|
+
{:else}
|
|
91
|
+
<Heading size="medium" color="success">
|
|
92
|
+
{#if run.type === "restore"}Restored{:else if run.type === "forget"}Pruned{:else}Backed
|
|
93
|
+
up{/if} in {duration}
|
|
94
|
+
</Heading>
|
|
95
|
+
{/if}
|
|
96
|
+
|
|
97
|
+
{#if run}
|
|
98
|
+
<Text color="secondary">
|
|
99
|
+
Started <RelativeTime time={run.start} />
|
|
100
|
+
</Text>
|
|
101
|
+
{/if}
|
|
102
|
+
</Stack>
|
|
103
|
+
|
|
104
|
+
{#if running}
|
|
105
|
+
<ProgressBar progress={log.status.progress} size="large">
|
|
106
|
+
{#if log.status.text}
|
|
107
|
+
<Text
|
|
108
|
+
size="small"
|
|
109
|
+
class={log.status.progress > 0.5 ? "text-light" : "text-dark"}
|
|
110
|
+
>
|
|
111
|
+
{log.status.text}
|
|
112
|
+
</Text>
|
|
113
|
+
{/if}
|
|
114
|
+
</ProgressBar>
|
|
115
|
+
{/if}
|
|
116
|
+
|
|
117
|
+
{#if log.errors.length > 0}
|
|
118
|
+
<Stack gap={2}>
|
|
119
|
+
<Alert color="danger">{log.errors[0]}</Alert>
|
|
120
|
+
{#if log.errors.length > 1}
|
|
121
|
+
<Scrollable class="max-h-32">
|
|
122
|
+
<Stack gap={1}>
|
|
123
|
+
{#each log.errors.slice(1) as error}
|
|
124
|
+
<Alert color="danger">{error}</Alert>
|
|
125
|
+
{/each}
|
|
126
|
+
</Stack>
|
|
127
|
+
</Scrollable>
|
|
128
|
+
{/if}
|
|
129
|
+
</Stack>
|
|
130
|
+
{/if}
|
|
131
|
+
|
|
132
|
+
{#if log.summary && run?.type !== "forget"}
|
|
133
|
+
<Stack gap={1}>
|
|
134
|
+
{#if run?.type === "restore"}
|
|
135
|
+
<Text>
|
|
136
|
+
{(log.summary.files_restored ?? 0).toLocaleString()} restored
|
|
137
|
+
{#if log.summary.files_skipped}
|
|
138
|
+
· {log.summary.files_skipped.toLocaleString()} skipped
|
|
139
|
+
{/if}
|
|
140
|
+
{#if log.summary.files_deleted}
|
|
141
|
+
· {log.summary.files_deleted.toLocaleString()} deleted
|
|
142
|
+
{/if}
|
|
143
|
+
</Text>
|
|
144
|
+
{#if log.summary.bytes_restored !== undefined}
|
|
145
|
+
<Text color="secondary">
|
|
146
|
+
Restored <FormatBytes bytes={log.summary.bytes_restored} />
|
|
147
|
+
{#if log.summary.total_bytes !== undefined}
|
|
148
|
+
of
|
|
149
|
+
<FormatBytes
|
|
150
|
+
bytes={Math.max(
|
|
151
|
+
log.summary.bytes_restored,
|
|
152
|
+
log.summary.total_bytes,
|
|
153
|
+
)}
|
|
154
|
+
/>
|
|
155
|
+
{/if}
|
|
156
|
+
</Text>
|
|
157
|
+
{/if}
|
|
158
|
+
{:else}
|
|
159
|
+
<Text>
|
|
160
|
+
{(log.summary.files_new ?? 0).toLocaleString()} new ·
|
|
161
|
+
{(log.summary.files_changed ?? 0).toLocaleString()} changed ·
|
|
162
|
+
{(log.summary.files_unmodified ?? 0).toLocaleString()} unchanged
|
|
163
|
+
</Text>
|
|
164
|
+
{#if log.summary.data_added !== undefined}
|
|
165
|
+
<Text color="secondary">
|
|
166
|
+
Added <FormatBytes bytes={log.summary.data_added} />
|
|
167
|
+
{#if log.summary.total_bytes_processed !== undefined}
|
|
168
|
+
of
|
|
169
|
+
<FormatBytes
|
|
170
|
+
bytes={Math.max(
|
|
171
|
+
log.summary.data_added,
|
|
172
|
+
log.summary.total_bytes_processed,
|
|
173
|
+
)}
|
|
174
|
+
/> processed
|
|
175
|
+
{/if}
|
|
176
|
+
</Text>
|
|
177
|
+
{/if}
|
|
178
|
+
{/if}
|
|
179
|
+
</Stack>
|
|
180
|
+
{/if}
|
|
181
|
+
|
|
182
|
+
{#if running}
|
|
183
|
+
<Stack gap={1} class="h-20 overflow-hidden">
|
|
184
|
+
{#each log.status.currentFiles.slice(0, 3) as file}
|
|
185
|
+
<Text size="small" color="secondary" class="truncate" title={file}>
|
|
186
|
+
{file}
|
|
187
|
+
</Text>
|
|
188
|
+
{/each}
|
|
189
|
+
</Stack>
|
|
190
|
+
{/if}
|
|
191
|
+
|
|
192
|
+
{#if $advanced}
|
|
193
|
+
<Stack gap={1}>
|
|
194
|
+
<Heading size="small">Event Log</Heading>
|
|
195
|
+
<Scrollable class="h-80 overflow-x-hidden">
|
|
196
|
+
<Stack gap={1}>
|
|
197
|
+
{#each log.events as event}
|
|
198
|
+
<Text size="tiny" class="font-mono select-all">
|
|
199
|
+
{JSON.stringify(event)}
|
|
200
|
+
</Text>
|
|
201
|
+
{/each}
|
|
202
|
+
</Stack>
|
|
203
|
+
</Scrollable>
|
|
204
|
+
</Stack>
|
|
205
|
+
{/if}
|
|
206
|
+
</Stack>
|
|
207
|
+
</ModalBody>
|
|
208
|
+
</Modal>
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { LocalRepositoryDto } from "../../../fetch-client";
|
|
3
|
+
import { useMetricsHistory } from "../../../services/metricsHistory.service";
|
|
4
|
+
import { formatDuration } from "../../../utils/format";
|
|
5
|
+
import { getReadableErrorMessage } from "../../../utils/handle-error";
|
|
6
|
+
import {
|
|
7
|
+
Alert,
|
|
8
|
+
Badge,
|
|
9
|
+
getByteUnitString,
|
|
10
|
+
HStack,
|
|
11
|
+
Icon,
|
|
12
|
+
LoadingSpinner,
|
|
13
|
+
Modal,
|
|
14
|
+
ModalBody,
|
|
15
|
+
Stack,
|
|
16
|
+
Text,
|
|
17
|
+
} from "@immich/ui";
|
|
18
|
+
import {
|
|
19
|
+
mdiAlertCircleOutline,
|
|
20
|
+
mdiCheckCircleOutline,
|
|
21
|
+
mdiDatabaseOutline,
|
|
22
|
+
mdiPlayCircleOutline,
|
|
23
|
+
mdiTimerOutline,
|
|
24
|
+
} from "@mdi/js";
|
|
25
|
+
import RelativeTime from "../../util/RelativeTime.svelte";
|
|
26
|
+
|
|
27
|
+
type Props = {
|
|
28
|
+
repository: LocalRepositoryDto;
|
|
29
|
+
onClose: () => void;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const { repository, onClose }: Props = $props();
|
|
33
|
+
|
|
34
|
+
// svelte-ignore state_referenced_locally
|
|
35
|
+
const query = useMetricsHistory(repository.id);
|
|
36
|
+
|
|
37
|
+
const entries = $derived(
|
|
38
|
+
query.data?.pages.flatMap((page) => page.items) ?? [],
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
const isoDate = (value: string) => new Date(value).toLocaleString();
|
|
42
|
+
|
|
43
|
+
let sentinel = $state<HTMLDivElement | null>(null);
|
|
44
|
+
|
|
45
|
+
$effect(() => {
|
|
46
|
+
if (!sentinel) return;
|
|
47
|
+
const observer = new IntersectionObserver(
|
|
48
|
+
([entry]) => {
|
|
49
|
+
if (
|
|
50
|
+
entry.isIntersecting &&
|
|
51
|
+
query.hasNextPage &&
|
|
52
|
+
!query.isFetchingNextPage
|
|
53
|
+
) {
|
|
54
|
+
void query.fetchNextPage();
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
{ rootMargin: "200px" },
|
|
58
|
+
);
|
|
59
|
+
observer.observe(sentinel);
|
|
60
|
+
return () => observer.disconnect();
|
|
61
|
+
});
|
|
62
|
+
</script>
|
|
63
|
+
|
|
64
|
+
<Modal title={`Metrics history for ${repository.name}`} size="large" {onClose}>
|
|
65
|
+
<ModalBody>
|
|
66
|
+
{#if query.isLoading}
|
|
67
|
+
<LoadingSpinner />
|
|
68
|
+
{:else if query.isError}
|
|
69
|
+
<Alert color="danger">{getReadableErrorMessage(query.error)}</Alert>
|
|
70
|
+
{:else if entries.length === 0}
|
|
71
|
+
<Text color="secondary" class="text-center py-6">
|
|
72
|
+
No metrics history yet.
|
|
73
|
+
</Text>
|
|
74
|
+
{:else}
|
|
75
|
+
<Stack gap={0} class="divide-y rounded-2xl border overflow-hidden">
|
|
76
|
+
{#each entries as entry (entry.id)}
|
|
77
|
+
{@const isStart = entry.started != null}
|
|
78
|
+
{@const isEnd = entry.backup != null}
|
|
79
|
+
{@const isSize = entry.sizeBytes != null && !isStart && !isEnd}
|
|
80
|
+
{@const succeeded =
|
|
81
|
+
isEnd &&
|
|
82
|
+
entry.successfulBackup != null &&
|
|
83
|
+
entry.successfulBackup === entry.backup}
|
|
84
|
+
<Stack gap={1} class="px-4 py-3">
|
|
85
|
+
<HStack class="justify-between gap-4">
|
|
86
|
+
<HStack class="gap-2">
|
|
87
|
+
{#if isStart}
|
|
88
|
+
<Icon
|
|
89
|
+
icon={mdiPlayCircleOutline}
|
|
90
|
+
size="18"
|
|
91
|
+
class="text-info-500"
|
|
92
|
+
/>
|
|
93
|
+
<Text>Backup started</Text>
|
|
94
|
+
{:else if isEnd && succeeded}
|
|
95
|
+
<Icon
|
|
96
|
+
icon={mdiCheckCircleOutline}
|
|
97
|
+
size="18"
|
|
98
|
+
class="text-success-500"
|
|
99
|
+
/>
|
|
100
|
+
<Text>Backup finished</Text>
|
|
101
|
+
<Badge size="tiny" color="success">Success</Badge>
|
|
102
|
+
{:else if isEnd}
|
|
103
|
+
<Icon
|
|
104
|
+
icon={mdiAlertCircleOutline}
|
|
105
|
+
size="18"
|
|
106
|
+
class="text-danger-500"
|
|
107
|
+
/>
|
|
108
|
+
<Text>Backup finished</Text>
|
|
109
|
+
<Badge size="tiny" color="danger">Failed</Badge>
|
|
110
|
+
{:else if isSize}
|
|
111
|
+
<Icon
|
|
112
|
+
icon={mdiDatabaseOutline}
|
|
113
|
+
size="18"
|
|
114
|
+
class="text-info-500"
|
|
115
|
+
/>
|
|
116
|
+
<Text>Size updated</Text>
|
|
117
|
+
{:else}
|
|
118
|
+
<Icon icon={mdiTimerOutline} size="18" color="secondary" />
|
|
119
|
+
<Text>Event</Text>
|
|
120
|
+
{/if}
|
|
121
|
+
</HStack>
|
|
122
|
+
<Text
|
|
123
|
+
color="secondary"
|
|
124
|
+
size="small"
|
|
125
|
+
title={isoDate(entry.createdAt)}
|
|
126
|
+
>
|
|
127
|
+
<RelativeTime time={entry.createdAt} />
|
|
128
|
+
</Text>
|
|
129
|
+
</HStack>
|
|
130
|
+
|
|
131
|
+
<HStack class="gap-2 flex-wrap pl-7">
|
|
132
|
+
{#if entry.backupDuration != null}
|
|
133
|
+
<Badge size="tiny" color="secondary">
|
|
134
|
+
Duration {formatDuration(entry.backupDuration)}
|
|
135
|
+
</Badge>
|
|
136
|
+
{/if}
|
|
137
|
+
{#if entry.sizeBytes != null}
|
|
138
|
+
<Badge size="tiny" color="secondary">
|
|
139
|
+
Size {getByteUnitString(entry.sizeBytes)}
|
|
140
|
+
</Badge>
|
|
141
|
+
{/if}
|
|
142
|
+
{#if entry.started}
|
|
143
|
+
<Badge size="tiny" color="secondary">
|
|
144
|
+
Started {isoDate(entry.started)}
|
|
145
|
+
</Badge>
|
|
146
|
+
{/if}
|
|
147
|
+
{#if entry.backup}
|
|
148
|
+
<Badge size="tiny" color="secondary">
|
|
149
|
+
Ended {isoDate(entry.backup)}
|
|
150
|
+
</Badge>
|
|
151
|
+
{/if}
|
|
152
|
+
</HStack>
|
|
153
|
+
</Stack>
|
|
154
|
+
{/each}
|
|
155
|
+
</Stack>
|
|
156
|
+
|
|
157
|
+
{#if query.hasNextPage}
|
|
158
|
+
<div bind:this={sentinel} class="flex justify-center py-4">
|
|
159
|
+
{#if query.isFetchingNextPage}
|
|
160
|
+
<LoadingSpinner />
|
|
161
|
+
{/if}
|
|
162
|
+
</div>
|
|
163
|
+
{/if}
|
|
164
|
+
{/if}
|
|
165
|
+
</ModalBody>
|
|
166
|
+
</Modal>
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { LocalRepositoryDto } from "../../../fetch-client";
|
|
2
|
+
type Props = {
|
|
3
|
+
repository: LocalRepositoryDto;
|
|
4
|
+
onClose: () => void;
|
|
5
|
+
};
|
|
6
|
+
declare const MetricsHistoryModal: import("svelte").Component<Props, {}, "">;
|
|
7
|
+
type MetricsHistoryModal = ReturnType<typeof MetricsHistoryModal>;
|
|
8
|
+
export default MetricsHistoryModal;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import StackList from "../../ui/StackList.svelte";
|
|
3
|
+
import OnEvents from "../../util/OnEvents.svelte";
|
|
4
|
+
import type { LocalRepositoryDto } from "../../../fetch-client";
|
|
5
|
+
import {
|
|
6
|
+
useRunEventHandler,
|
|
7
|
+
useRunHistory,
|
|
8
|
+
} from "../../../services/runHistory.service";
|
|
9
|
+
import { Text } from "@immich/ui";
|
|
10
|
+
import RepositoryRunHistoryItem from "./RepositoryRunHistoryItem.svelte";
|
|
11
|
+
|
|
12
|
+
type Props = {
|
|
13
|
+
repository: LocalRepositoryDto;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
let { repository }: Props = $props();
|
|
17
|
+
|
|
18
|
+
// svelte-ignore state_referenced_locally
|
|
19
|
+
const query = useRunHistory(repository.id);
|
|
20
|
+
const { onRunCreate, onRunUpdate } = useRunEventHandler();
|
|
21
|
+
</script>
|
|
22
|
+
|
|
23
|
+
<OnEvents {onRunCreate} {onRunUpdate} />
|
|
24
|
+
|
|
25
|
+
<StackList {query}>
|
|
26
|
+
{#snippet title()}
|
|
27
|
+
Recent backup attempts
|
|
28
|
+
{/snippet}
|
|
29
|
+
|
|
30
|
+
{#snippet children(runs)}
|
|
31
|
+
{#if runs.length === 0}
|
|
32
|
+
<Text class="text-center py-6" color="muted">No recent backups</Text>
|
|
33
|
+
{:else}
|
|
34
|
+
{#each runs.slice(0, 5) as run (run.id)}
|
|
35
|
+
<RepositoryRunHistoryItem {run} />
|
|
36
|
+
{/each}
|
|
37
|
+
{/if}
|
|
38
|
+
{/snippet}
|
|
39
|
+
</StackList>
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { LocalRepositoryDto } from "../../../fetch-client";
|
|
2
|
+
type Props = {
|
|
3
|
+
repository: LocalRepositoryDto;
|
|
4
|
+
};
|
|
5
|
+
declare const RepositoryRunHistory: import("svelte").Component<Props, {}, "">;
|
|
6
|
+
type RepositoryRunHistory = ReturnType<typeof RepositoryRunHistory>;
|
|
7
|
+
export default RepositoryRunHistory;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import StackListItem from "../../ui/StackListItem.svelte";
|
|
3
|
+
import RelativeTime from "../../util/RelativeTime.svelte";
|
|
4
|
+
import type { RunDto } from "../../../fetch-client";
|
|
5
|
+
import { getRunActions } from "../../../services/runHistory.service";
|
|
6
|
+
import { Icon } from "@immich/ui";
|
|
7
|
+
import {
|
|
8
|
+
mdiAlertCircleOutline,
|
|
9
|
+
mdiCheckCircleOutline,
|
|
10
|
+
mdiLoading,
|
|
11
|
+
} from "@mdi/js";
|
|
12
|
+
|
|
13
|
+
type Props = {
|
|
14
|
+
run: RunDto;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const { run }: Props = $props();
|
|
18
|
+
const { ViewLog } = $derived(getRunActions(run));
|
|
19
|
+
</script>
|
|
20
|
+
|
|
21
|
+
<StackListItem actions={[ViewLog]}>
|
|
22
|
+
{#snippet icon()}
|
|
23
|
+
{#if run.status === "complete"}
|
|
24
|
+
<Icon icon={mdiCheckCircleOutline} class="text-success-500" />
|
|
25
|
+
{:else if run.status === "failed"}
|
|
26
|
+
<Icon icon={mdiAlertCircleOutline} class="text-danger-500" />
|
|
27
|
+
{:else}
|
|
28
|
+
<Icon icon={mdiLoading} class="animate-spin opacity-60" />
|
|
29
|
+
{/if}
|
|
30
|
+
{/snippet}
|
|
31
|
+
|
|
32
|
+
{#if run.status === "incomplete"}
|
|
33
|
+
{#if run.type === "restore"}Restoring{:else if run.type === "forget"}Pruning{:else}Backing
|
|
34
|
+
up{/if} · started
|
|
35
|
+
<RelativeTime time={run.start} />
|
|
36
|
+
{:else if run.status === "failed"}
|
|
37
|
+
{#if run.type === "restore"}Restore{:else if run.type === "forget"}Prune{:else}Backup{/if}
|
|
38
|
+
failed
|
|
39
|
+
{#if run.end}<RelativeTime time={run.end} />{/if}
|
|
40
|
+
{:else}
|
|
41
|
+
{#if run.type === "restore"}Restored{:else if run.type === "forget"}Pruned{:else}Backed
|
|
42
|
+
up{/if}
|
|
43
|
+
{#if run.end}<RelativeTime time={run.end} />{/if}
|
|
44
|
+
{/if}
|
|
45
|
+
</StackListItem>
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { RunDto } from "../../../fetch-client";
|
|
2
|
+
type Props = {
|
|
3
|
+
run: RunDto;
|
|
4
|
+
};
|
|
5
|
+
declare const RepositoryRunHistoryItem: import("svelte").Component<Props, {}, "">;
|
|
6
|
+
type RepositoryRunHistoryItem = ReturnType<typeof RepositoryRunHistoryItem>;
|
|
7
|
+
export default RepositoryRunHistoryItem;
|