mirror of
https://github.com/immich-app/immich.git
synced 2025-12-18 09:13:15 +03:00
refactor: api key modals (#23420)
This commit is contained in:
78
web/src/lib/components/ApiKeyPermissionsPicker.svelte
Normal file
78
web/src/lib/components/ApiKeyPermissionsPicker.svelte
Normal file
@@ -0,0 +1,78 @@
|
||||
<script lang="ts">
|
||||
import ApiKeyGrid from '$lib/components/user-settings-page/user-api-key-grid.svelte';
|
||||
import { Permission } from '@immich/sdk';
|
||||
import { Checkbox, IconButton, Input, Label } from '@immich/ui';
|
||||
import { mdiClose } from '@mdi/js';
|
||||
import { t } from 'svelte-i18n';
|
||||
|
||||
type Props = {
|
||||
selectedPermissions: Permission[];
|
||||
};
|
||||
|
||||
let { selectedPermissions = $bindable([]) }: Props = $props();
|
||||
|
||||
const permissions: Record<string, Permission[]> = {};
|
||||
for (const permission of Object.values(Permission)) {
|
||||
if (permission === Permission.All) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const [group] = permission.split('.');
|
||||
if (!permissions[group]) {
|
||||
permissions[group] = [];
|
||||
}
|
||||
permissions[group].push(permission);
|
||||
}
|
||||
|
||||
let searchValue = $state('');
|
||||
let allItemsSelected = $derived(selectedPermissions.length === Object.keys(Permission).length - 1);
|
||||
|
||||
const matchFilter = (search: string) => {
|
||||
search = search.toLowerCase();
|
||||
|
||||
return ([title, items]: [string, Permission[]]) =>
|
||||
title.toLowerCase().includes(search) || items.some((item) => item.toLowerCase().includes(search));
|
||||
};
|
||||
|
||||
const onCheckedAllChange = (checked: boolean) => {
|
||||
selectedPermissions = checked
|
||||
? Object.values(Permission).filter((permission) => permission !== Permission.All)
|
||||
: [];
|
||||
};
|
||||
|
||||
const filteredResults = $derived(Object.entries(permissions).filter(matchFilter(searchValue)));
|
||||
|
||||
const handleSelectItems = (items: Permission[]) =>
|
||||
(selectedPermissions = Array.from(new Set([...selectedPermissions, ...items])));
|
||||
|
||||
const handleDeselectItems = (items: Permission[]) =>
|
||||
(selectedPermissions = selectedPermissions.filter((item) => !items.includes(item)));
|
||||
</script>
|
||||
|
||||
<Label label={$t('permission')} for="permission-container" />
|
||||
<div class="flex items-center gap-2 m-4" id="permission-container">
|
||||
<Checkbox id="input-select-all" size="tiny" checked={allItemsSelected} onCheckedChange={onCheckedAllChange} />
|
||||
<Label label={$t('select_all')} for="input-select-all" />
|
||||
</div>
|
||||
|
||||
<div class="ms-4 flex flex-col gap-2">
|
||||
<Input bind:value={searchValue} placeholder={$t('search')}>
|
||||
{#snippet trailingIcon()}
|
||||
{#if searchValue}
|
||||
<IconButton
|
||||
icon={mdiClose}
|
||||
size="small"
|
||||
variant="ghost"
|
||||
shape="round"
|
||||
color="secondary"
|
||||
class="me-1"
|
||||
onclick={() => (searchValue = '')}
|
||||
aria-label={$t('clear')}
|
||||
/>
|
||||
{/if}
|
||||
{/snippet}
|
||||
</Input>
|
||||
{#each filteredResults as [title, subItems] (title)}
|
||||
<ApiKeyGrid {title} {subItems} selectedItems={selectedPermissions} {handleSelectItems} {handleDeselectItems} />
|
||||
{/each}
|
||||
</div>
|
||||
@@ -1,10 +1,11 @@
|
||||
<script lang="ts">
|
||||
import { dateFormats } from '$lib/constants';
|
||||
import ApiKeyModal from '$lib/modals/ApiKeyModal.svelte';
|
||||
import ApiKeyCreateModal from '$lib/modals/ApiKeyCreateModal.svelte';
|
||||
import ApiKeySecretModal from '$lib/modals/ApiKeySecretModal.svelte';
|
||||
import ApiKeyUpdateModal from '$lib/modals/ApiKeyUpdateModal.svelte';
|
||||
import { locale } from '$lib/stores/preferences.store';
|
||||
import { handleError } from '$lib/utils/handle-error';
|
||||
import { createApiKey, deleteApiKey, getApiKeys, updateApiKey, type ApiKeyResponseDto } from '@immich/sdk';
|
||||
import { deleteApiKey, getApiKeys, type ApiKeyResponseDto } from '@immich/sdk';
|
||||
import { Button, IconButton, modalManager, toastManager } from '@immich/ui';
|
||||
import { mdiPencilOutline, mdiTrashCanOutline } from '@mdi/js';
|
||||
import { t } from 'svelte-i18n';
|
||||
@@ -21,49 +22,22 @@
|
||||
}
|
||||
|
||||
const handleCreate = async () => {
|
||||
const result = await modalManager.show(ApiKeyModal, {
|
||||
title: $t('new_api_key'),
|
||||
apiKey: { name: 'API Key', permissions: [] },
|
||||
submitText: $t('create'),
|
||||
});
|
||||
const secret = await modalManager.show(ApiKeyCreateModal);
|
||||
|
||||
if (!result) {
|
||||
if (!secret) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const { secret } = await createApiKey({
|
||||
apiKeyCreateDto: {
|
||||
name: result.name,
|
||||
permissions: result.permissions,
|
||||
},
|
||||
});
|
||||
|
||||
await modalManager.show(ApiKeySecretModal, { secret });
|
||||
} catch (error) {
|
||||
handleError(error, $t('errors.unable_to_create_api_key'));
|
||||
} finally {
|
||||
await refreshKeys();
|
||||
}
|
||||
await modalManager.show(ApiKeySecretModal, { secret });
|
||||
await refreshKeys();
|
||||
};
|
||||
|
||||
const handleUpdate = async (key: ApiKeyResponseDto) => {
|
||||
const result = await modalManager.show(ApiKeyModal, {
|
||||
title: $t('api_key'),
|
||||
submitText: $t('save'),
|
||||
const success = await modalManager.show(ApiKeyUpdateModal, {
|
||||
apiKey: key,
|
||||
});
|
||||
|
||||
if (!result) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await updateApiKey({ id: key.id, apiKeyUpdateDto: { name: result.name, permissions: result.permissions } });
|
||||
toastManager.success($t('saved_api_key'));
|
||||
} catch (error) {
|
||||
handleError(error, $t('errors.unable_to_save_api_key'));
|
||||
} finally {
|
||||
if (success) {
|
||||
await refreshKeys();
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user