mirror of
https://github.com/immich-app/immich.git
synced 2025-05-17 05:02:16 +02:00
refactor: job create modal (#18106)
* refactor: job create modal * chore: better modal manager types (#18107)
This commit is contained in:
parent
33f3751b72
commit
1138f6dcce
4 changed files with 83 additions and 68 deletions
web/src
lib
routes/admin
|
@ -2,11 +2,14 @@ import ConfirmDialog from '$lib/components/shared-components/dialog/confirm-dial
|
|||
import { mount, unmount, type Component, type ComponentProps } from 'svelte';
|
||||
|
||||
type OnCloseData<T> = T extends { onClose: (data: infer R) => void } ? R : never;
|
||||
// TODO make `props` optional if component only has `onClose`
|
||||
// type OptionalIfEmpty<T extends object> = keyof T extends never ? undefined : T;
|
||||
type OptionalIfEmpty<T extends object> = keyof T extends never ? undefined : T;
|
||||
|
||||
class ModalManager {
|
||||
open<T extends object, K = OnCloseData<T>>(Component: Component<T>, props: Omit<T, 'onClose'>) {
|
||||
open<T extends object, K = OnCloseData<T>>(
|
||||
Component: Component<T>,
|
||||
props?: OptionalIfEmpty<Omit<T, 'onClose'>> | Record<string, never>,
|
||||
): Promise<K>;
|
||||
open<T extends object, K = OnCloseData<T>>(Component: Component<T>, props: OptionalIfEmpty<Omit<T, 'onClose'>>) {
|
||||
return new Promise<K>((resolve) => {
|
||||
let modal: object = {};
|
||||
|
||||
|
@ -18,7 +21,7 @@ class ModalManager {
|
|||
modal = mount(Component, {
|
||||
target: document.body,
|
||||
props: {
|
||||
...(props as T),
|
||||
...((props ?? {}) as T),
|
||||
onClose,
|
||||
},
|
||||
});
|
||||
|
|
65
web/src/lib/modals/JobCreateModal.svelte
Normal file
65
web/src/lib/modals/JobCreateModal.svelte
Normal file
|
@ -0,0 +1,65 @@
|
|||
<script lang="ts">
|
||||
import Combobox, { type ComboBoxOption } from '$lib/components/shared-components/combobox.svelte';
|
||||
import ConfirmDialog from '$lib/components/shared-components/dialog/confirm-dialog.svelte';
|
||||
import {
|
||||
notificationController,
|
||||
NotificationType,
|
||||
} from '$lib/components/shared-components/notification/notification';
|
||||
import { handleError } from '$lib/utils/handle-error';
|
||||
import { createJob, ManualJobName } from '@immich/sdk';
|
||||
import { t } from 'svelte-i18n';
|
||||
|
||||
type Props = { onClose: (confirmed: boolean) => void };
|
||||
|
||||
let { onClose }: Props = $props();
|
||||
|
||||
const options = [
|
||||
{ title: $t('admin.person_cleanup_job'), value: ManualJobName.PersonCleanup },
|
||||
{ title: $t('admin.tag_cleanup_job'), value: ManualJobName.TagCleanup },
|
||||
{ title: $t('admin.user_cleanup_job'), value: ManualJobName.UserCleanup },
|
||||
{ title: $t('admin.memory_cleanup_job'), value: ManualJobName.MemoryCleanup },
|
||||
{ title: $t('admin.memory_generate_job'), value: ManualJobName.MemoryCreate },
|
||||
{ title: $t('admin.backup_database'), value: ManualJobName.BackupDatabase },
|
||||
].map(({ value, title }) => ({ id: value, label: title, value }));
|
||||
|
||||
let selectedJob: ComboBoxOption | undefined = $state(undefined);
|
||||
|
||||
const onsubmit = async (event: Event) => {
|
||||
event.preventDefault();
|
||||
await handleCreate();
|
||||
};
|
||||
|
||||
const handleCreate = async () => {
|
||||
if (!selectedJob) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await createJob({ jobCreateDto: { name: selectedJob.value as ManualJobName } });
|
||||
notificationController.show({ message: $t('admin.job_created'), type: NotificationType.Info });
|
||||
onClose(true);
|
||||
} catch (error) {
|
||||
handleError(error, $t('errors.unable_to_submit_job'));
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<ConfirmDialog
|
||||
confirmColor="primary"
|
||||
title={$t('admin.create_job')}
|
||||
disabled={!selectedJob}
|
||||
onClose={(confirmed) => (confirmed ? handleCreate() : onClose(false))}
|
||||
>
|
||||
{#snippet promptSnippet()}
|
||||
<form {onsubmit} autocomplete="off" id="create-tag-form" class="w-full">
|
||||
<div class="flex flex-col gap-1 text-start">
|
||||
<Combobox
|
||||
bind:selectedOption={selectedJob}
|
||||
label={$t('jobs')}
|
||||
{options}
|
||||
placeholder={$t('admin.search_jobs')}
|
||||
/>
|
||||
</div>
|
||||
</form>
|
||||
{/snippet}
|
||||
</ConfirmDialog>
|
|
@ -1,16 +1,11 @@
|
|||
<script lang="ts">
|
||||
import JobsPanel from '$lib/components/admin-page/jobs/jobs-panel.svelte';
|
||||
import UserPageLayout from '$lib/components/layouts/user-page-layout.svelte';
|
||||
import Combobox, { type ComboBoxOption } from '$lib/components/shared-components/combobox.svelte';
|
||||
import ConfirmDialog from '$lib/components/shared-components/dialog/confirm-dialog.svelte';
|
||||
import {
|
||||
notificationController,
|
||||
NotificationType,
|
||||
} from '$lib/components/shared-components/notification/notification';
|
||||
import { AppRoute } from '$lib/constants';
|
||||
import { modalManager } from '$lib/managers/modal-manager.svelte';
|
||||
import JobCreateModal from '$lib/modals/JobCreateModal.svelte';
|
||||
import { asyncTimeout } from '$lib/utils';
|
||||
import { handleError } from '$lib/utils/handle-error';
|
||||
import { createJob, getAllJobsStatus, ManualJobName, type AllJobStatusResponseDto } from '@immich/sdk';
|
||||
import { getAllJobsStatus, type AllJobStatusResponseDto } from '@immich/sdk';
|
||||
import { Button, HStack, Text } from '@immich/ui';
|
||||
import { mdiCog, mdiPlus } from '@mdi/js';
|
||||
import { onDestroy, onMount } from 'svelte';
|
||||
|
@ -26,8 +21,6 @@
|
|||
let jobs: AllJobStatusResponseDto | undefined = $state();
|
||||
|
||||
let running = true;
|
||||
let isOpen = $state(false);
|
||||
let selectedJob: ComboBoxOption | undefined = $state(undefined);
|
||||
|
||||
onMount(async () => {
|
||||
while (running) {
|
||||
|
@ -39,42 +32,18 @@
|
|||
onDestroy(() => {
|
||||
running = false;
|
||||
});
|
||||
|
||||
const options = [
|
||||
{ title: $t('admin.person_cleanup_job'), value: ManualJobName.PersonCleanup },
|
||||
{ title: $t('admin.tag_cleanup_job'), value: ManualJobName.TagCleanup },
|
||||
{ title: $t('admin.user_cleanup_job'), value: ManualJobName.UserCleanup },
|
||||
{ title: $t('admin.memory_cleanup_job'), value: ManualJobName.MemoryCleanup },
|
||||
{ title: $t('admin.memory_generate_job'), value: ManualJobName.MemoryCreate },
|
||||
{ title: $t('admin.backup_database'), value: ManualJobName.BackupDatabase },
|
||||
].map(({ value, title }) => ({ id: value, label: title, value }));
|
||||
|
||||
const handleCancel = () => (isOpen = false);
|
||||
|
||||
const handleCreate = async () => {
|
||||
if (!selectedJob) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await createJob({ jobCreateDto: { name: selectedJob.value as ManualJobName } });
|
||||
notificationController.show({ message: $t('admin.job_created'), type: NotificationType.Info });
|
||||
handleCancel();
|
||||
} catch (error) {
|
||||
handleError(error, $t('errors.unable_to_submit_job'));
|
||||
}
|
||||
};
|
||||
|
||||
const onsubmit = async (event: Event) => {
|
||||
event.preventDefault();
|
||||
await handleCreate();
|
||||
};
|
||||
</script>
|
||||
|
||||
<UserPageLayout title={data.meta.title} admin>
|
||||
{#snippet buttons()}
|
||||
<HStack gap={0}>
|
||||
<Button leadingIcon={mdiPlus} onclick={() => (isOpen = true)} size="small" variant="ghost" color="secondary">
|
||||
<Button
|
||||
leadingIcon={mdiPlus}
|
||||
onclick={() => modalManager.open(JobCreateModal)}
|
||||
size="small"
|
||||
variant="ghost"
|
||||
color="secondary"
|
||||
>
|
||||
<Text class="hidden md:block">{$t('admin.create_job')}</Text>
|
||||
</Button>
|
||||
<Button
|
||||
|
@ -96,25 +65,3 @@
|
|||
</section>
|
||||
</section>
|
||||
</UserPageLayout>
|
||||
|
||||
{#if isOpen}
|
||||
<ConfirmDialog
|
||||
confirmColor="primary"
|
||||
title={$t('admin.create_job')}
|
||||
disabled={!selectedJob}
|
||||
onClose={(confirmed) => (confirmed ? handleCreate() : handleCancel())}
|
||||
>
|
||||
{#snippet promptSnippet()}
|
||||
<form {onsubmit} autocomplete="off" id="create-tag-form" class="w-full">
|
||||
<div class="flex flex-col gap-1 text-start">
|
||||
<Combobox
|
||||
bind:selectedOption={selectedJob}
|
||||
label={$t('jobs')}
|
||||
{options}
|
||||
placeholder={$t('admin.search_jobs')}
|
||||
/>
|
||||
</div>
|
||||
</form>
|
||||
{/snippet}
|
||||
</ConfirmDialog>
|
||||
{/if}
|
||||
|
|
|
@ -59,7 +59,7 @@
|
|||
};
|
||||
|
||||
const handleCreate = async () => {
|
||||
await modalManager.open(UserCreateModal, {});
|
||||
await modalManager.open(UserCreateModal);
|
||||
await refresh();
|
||||
};
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue