feat: consistent IconGridSelect sizing + descriptions + filter upgrades
- Added desc text to all 40+ grid items (EN + RU) - compact prop on all IconGridSelect in compact form sections - Fixed compact width to fill grid cells (removed width:auto) - Replaced <select> filter dropdowns with IconGridSelect on config pages - Replaced <select> provider filters with EntitySelect on tracker pages - Dashboard filters constrained to fixed widths (not full row) - Added filtering to command-template-configs and providers pages - providerTypeFilterItems() with "All" option for filter contexts
This commit is contained in:
@@ -138,10 +138,9 @@
|
|||||||
border-color: var(--color-primary);
|
border-color: var(--color-primary);
|
||||||
}
|
}
|
||||||
.icon-grid-compact {
|
.icon-grid-compact {
|
||||||
width: auto;
|
padding: 0.25rem 0.5rem;
|
||||||
padding: 0.3rem 0.5rem;
|
|
||||||
gap: 0.3rem;
|
gap: 0.3rem;
|
||||||
font-size: 0.8rem;
|
font-size: 0.875rem;
|
||||||
}
|
}
|
||||||
.icon-grid-compact .icon-grid-trigger-label {
|
.icon-grid-compact .icon-grid-trigger-label {
|
||||||
flex: none;
|
flex: none;
|
||||||
|
|||||||
@@ -9,96 +9,105 @@ import type { GridItem } from '$lib/components/IconGridSelect.svelte';
|
|||||||
// --- Sort ---
|
// --- Sort ---
|
||||||
|
|
||||||
export const sortByItems = (): GridItem[] => [
|
export const sortByItems = (): GridItem[] => [
|
||||||
{ value: 'none', icon: 'mdiMinus', label: t('trackingConfig.sortNone') },
|
{ value: 'none', icon: 'mdiMinus', label: t('trackingConfig.sortNone'), desc: t('gridDesc.sortNone') },
|
||||||
{ value: 'date', icon: 'mdiCalendar', label: t('trackingConfig.sortDate') },
|
{ value: 'date', icon: 'mdiCalendar', label: t('trackingConfig.sortDate'), desc: t('gridDesc.sortDate') },
|
||||||
{ value: 'rating', icon: 'mdiStar', label: t('trackingConfig.sortRating') },
|
{ value: 'rating', icon: 'mdiStar', label: t('trackingConfig.sortRating'), desc: t('gridDesc.sortRating') },
|
||||||
{ value: 'name', icon: 'mdiSortAlphabeticalAscending', label: t('trackingConfig.sortName') },
|
{ value: 'name', icon: 'mdiSortAlphabeticalAscending', label: t('trackingConfig.sortName'), desc: t('gridDesc.sortName') },
|
||||||
{ value: 'random', icon: 'mdiDice3', label: t('trackingConfig.sortRandom') },
|
{ value: 'random', icon: 'mdiDice3', label: t('trackingConfig.sortRandom'), desc: t('gridDesc.sortRandom') },
|
||||||
];
|
];
|
||||||
|
|
||||||
export const sortOrderItems = (): GridItem[] => [
|
export const sortOrderItems = (): GridItem[] => [
|
||||||
{ value: 'descending', icon: 'mdiSortDescending', label: t('trackingConfig.orderDesc') },
|
{ value: 'descending', icon: 'mdiSortDescending', label: t('trackingConfig.orderDesc'), desc: t('gridDesc.orderDesc') },
|
||||||
{ value: 'ascending', icon: 'mdiSortAscending', label: t('trackingConfig.orderAsc') },
|
{ value: 'ascending', icon: 'mdiSortAscending', label: t('trackingConfig.orderAsc'), desc: t('gridDesc.orderAsc') },
|
||||||
];
|
];
|
||||||
|
|
||||||
// --- Album mode ---
|
// --- Album mode ---
|
||||||
|
|
||||||
export const albumModeItems = (): GridItem[] => [
|
export const albumModeItems = (): GridItem[] => [
|
||||||
{ value: 'per_collection', icon: 'mdiViewGrid', label: t('trackingConfig.albumModePerAlbum') },
|
{ value: 'per_collection', icon: 'mdiViewGrid', label: t('trackingConfig.albumModePerAlbum'), desc: t('gridDesc.albumModePerAlbum') },
|
||||||
{ value: 'combined', icon: 'mdiSetMerge', label: t('trackingConfig.albumModeCombined') },
|
{ value: 'combined', icon: 'mdiSetMerge', label: t('trackingConfig.albumModeCombined'), desc: t('gridDesc.albumModeCombined') },
|
||||||
{ value: 'random', icon: 'mdiDice3', label: t('trackingConfig.albumModeRandom') },
|
{ value: 'random', icon: 'mdiDice3', label: t('trackingConfig.albumModeRandom'), desc: t('gridDesc.albumModeRandom') },
|
||||||
];
|
];
|
||||||
|
|
||||||
// --- Asset type ---
|
// --- Asset type ---
|
||||||
|
|
||||||
export const assetTypeItems = (): GridItem[] => [
|
export const assetTypeItems = (): GridItem[] => [
|
||||||
{ value: 'all', icon: 'mdiSelectAll', label: t('trackingConfig.assetTypeAll') },
|
{ value: 'all', icon: 'mdiSelectAll', label: t('trackingConfig.assetTypeAll'), desc: t('gridDesc.assetTypeAll') },
|
||||||
{ value: 'photo', icon: 'mdiImage', label: t('trackingConfig.assetTypePhoto') },
|
{ value: 'photo', icon: 'mdiImage', label: t('trackingConfig.assetTypePhoto'), desc: t('gridDesc.assetTypePhoto') },
|
||||||
{ value: 'video', icon: 'mdiVideo', label: t('trackingConfig.assetTypeVideo') },
|
{ value: 'video', icon: 'mdiVideo', label: t('trackingConfig.assetTypeVideo'), desc: t('gridDesc.assetTypeVideo') },
|
||||||
];
|
];
|
||||||
|
|
||||||
// --- Memory source ---
|
// --- Memory source ---
|
||||||
|
|
||||||
export const memorySourceItems = (): GridItem[] => [
|
export const memorySourceItems = (): GridItem[] => [
|
||||||
{ value: 'albums', icon: 'mdiImageMultiple', label: t('trackingConfig.memorySourceAlbums') },
|
{ value: 'albums', icon: 'mdiImageMultiple', label: t('trackingConfig.memorySourceAlbums'), desc: t('gridDesc.memorySourceAlbums') },
|
||||||
{ value: 'native', icon: 'mdiMemory', label: t('trackingConfig.memorySourceNative') },
|
{ value: 'native', icon: 'mdiMemory', label: t('trackingConfig.memorySourceNative'), desc: t('gridDesc.memorySourceNative') },
|
||||||
];
|
];
|
||||||
|
|
||||||
// --- Locale ---
|
// --- Locale ---
|
||||||
|
|
||||||
export const localeItems = (): GridItem[] => [
|
export const localeItems = (): GridItem[] => [
|
||||||
{ value: 'en', icon: 'mdiAlphabeticalVariant', label: 'English' },
|
{ value: 'en', icon: 'mdiAlphabeticalVariant', label: 'English', desc: t('gridDesc.localeEn') },
|
||||||
{ value: 'ru', icon: 'mdiAlphabeticalVariant', label: 'Русский' },
|
{ value: 'ru', icon: 'mdiAlphabeticalVariant', label: 'Русский', desc: t('gridDesc.localeRu') },
|
||||||
];
|
];
|
||||||
|
|
||||||
// --- Response mode ---
|
// --- Response mode ---
|
||||||
|
|
||||||
export const responseModeItems = (tFn: typeof t): GridItem[] => [
|
export const responseModeItems = (tFn: typeof t): GridItem[] => [
|
||||||
{ value: 'media', icon: 'mdiImage', label: tFn('commandConfig.modeMedia') },
|
{ value: 'media', icon: 'mdiImage', label: tFn('commandConfig.modeMedia'), desc: tFn('gridDesc.modeMedia') },
|
||||||
{ value: 'text', icon: 'mdiText', label: tFn('commandConfig.modeText') },
|
{ value: 'text', icon: 'mdiText', label: tFn('commandConfig.modeText'), desc: tFn('gridDesc.modeText') },
|
||||||
];
|
];
|
||||||
|
|
||||||
// --- Event type filter (dashboard) ---
|
// --- Event type filter (dashboard) ---
|
||||||
|
|
||||||
export const eventTypeFilterItems = (): GridItem[] => [
|
export const eventTypeFilterItems = (): GridItem[] => [
|
||||||
{ value: '', icon: 'mdiFilterOff', label: t('dashboard.allEvents') },
|
{ value: '', icon: 'mdiFilterOff', label: t('dashboard.allEvents'), desc: t('gridDesc.allEvents') },
|
||||||
{ value: 'assets_added', icon: 'mdiImagePlus', label: t('dashboard.filterAssetsAdded') },
|
{ value: 'assets_added', icon: 'mdiImagePlus', label: t('dashboard.filterAssetsAdded'), desc: t('gridDesc.assetsAdded') },
|
||||||
{ value: 'assets_removed', icon: 'mdiImageMinus', label: t('dashboard.filterAssetsRemoved') },
|
{ value: 'assets_removed', icon: 'mdiImageMinus', label: t('dashboard.filterAssetsRemoved'), desc: t('gridDesc.assetsRemoved') },
|
||||||
{ value: 'collection_renamed', icon: 'mdiRename', label: t('dashboard.filterRenamed') },
|
{ value: 'collection_renamed', icon: 'mdiRename', label: t('dashboard.filterRenamed'), desc: t('gridDesc.renamed') },
|
||||||
{ value: 'collection_deleted', icon: 'mdiDeleteAlert', label: t('dashboard.filterDeleted') },
|
{ value: 'collection_deleted', icon: 'mdiDeleteAlert', label: t('dashboard.filterDeleted'), desc: t('gridDesc.deleted') },
|
||||||
{ value: 'sharing_changed', icon: 'mdiShareVariant', label: t('dashboard.filterSharingChanged') },
|
{ value: 'sharing_changed', icon: 'mdiShareVariant', label: t('dashboard.filterSharingChanged'), desc: t('gridDesc.sharingChanged') },
|
||||||
];
|
];
|
||||||
|
|
||||||
// --- Sort filter (dashboard) ---
|
// --- Sort filter (dashboard) ---
|
||||||
|
|
||||||
export const sortFilterItems = (): GridItem[] => [
|
export const sortFilterItems = (): GridItem[] => [
|
||||||
{ value: 'newest', icon: 'mdiSortClockDescending', label: t('dashboard.newestFirst') },
|
{ value: 'newest', icon: 'mdiSortClockDescending', label: t('dashboard.newestFirst'), desc: t('gridDesc.newestFirst') },
|
||||||
{ value: 'oldest', icon: 'mdiSortClockAscending', label: t('dashboard.oldestFirst') },
|
{ value: 'oldest', icon: 'mdiSortClockAscending', label: t('dashboard.oldestFirst'), desc: t('gridDesc.oldestFirst') },
|
||||||
];
|
];
|
||||||
|
|
||||||
// --- Chat action (Telegram targets) ---
|
// --- Chat action (Telegram targets) ---
|
||||||
|
|
||||||
export const chatActionItems = (): GridItem[] => [
|
export const chatActionItems = (): GridItem[] => [
|
||||||
{ value: '', icon: 'mdiMinus', label: t('targets.chatActionNone') },
|
{ value: '', icon: 'mdiMinus', label: t('targets.chatActionNone'), desc: t('gridDesc.chatActionNone') },
|
||||||
{ value: 'typing', icon: 'mdiKeyboard', label: t('targets.chatActionTyping') },
|
{ value: 'typing', icon: 'mdiKeyboard', label: t('targets.chatActionTyping'), desc: t('gridDesc.chatActionTyping') },
|
||||||
{ value: 'upload_photo', icon: 'mdiImagePlus', label: t('targets.chatActionUploadPhoto') },
|
{ value: 'upload_photo', icon: 'mdiImagePlus', label: t('targets.chatActionUploadPhoto'), desc: t('gridDesc.chatActionUploadPhoto') },
|
||||||
{ value: 'upload_video', icon: 'mdiVideoPlus', label: t('targets.chatActionUploadVideo') },
|
{ value: 'upload_video', icon: 'mdiVideoPlus', label: t('targets.chatActionUploadVideo'), desc: t('gridDesc.chatActionUploadVideo') },
|
||||||
{ value: 'upload_document', icon: 'mdiFileUpload', label: t('targets.chatActionUploadDoc') },
|
{ value: 'upload_document', icon: 'mdiFileUpload', label: t('targets.chatActionUploadDoc'), desc: t('gridDesc.chatActionUploadDoc') },
|
||||||
{ value: 'record_video', icon: 'mdiVideo', label: t('targets.chatActionRecordVideo') },
|
{ value: 'record_video', icon: 'mdiVideo', label: t('targets.chatActionRecordVideo'), desc: t('gridDesc.chatActionRecordVideo') },
|
||||||
{ value: 'record_voice', icon: 'mdiMicrophone', label: t('targets.chatActionRecordVoice') },
|
{ value: 'record_voice', icon: 'mdiMicrophone', label: t('targets.chatActionRecordVoice'), desc: t('gridDesc.chatActionRecordVoice') },
|
||||||
];
|
];
|
||||||
|
|
||||||
// --- Preview target type ---
|
// --- Preview target type ---
|
||||||
|
|
||||||
export const previewTargetTypeItems = (): GridItem[] => [
|
export const previewTargetTypeItems = (): GridItem[] => [
|
||||||
{ value: 'telegram', icon: 'mdiSend', label: t('targets.typeTelegram') },
|
{ value: 'telegram', icon: 'mdiSend', label: t('targets.typeTelegram'), desc: t('gridDesc.previewTelegram') },
|
||||||
{ value: 'webhook', icon: 'mdiWebhook', label: t('targets.typeWebhook') },
|
{ value: 'webhook', icon: 'mdiWebhook', label: t('targets.typeWebhook'), desc: t('gridDesc.previewWebhook') },
|
||||||
|
];
|
||||||
|
|
||||||
|
// --- Provider type filter (with "All" option) ---
|
||||||
|
|
||||||
|
export const providerTypeFilterItems = (): GridItem[] => [
|
||||||
|
{ value: '', icon: 'mdiFilterOff', label: t('common.allTypes'), desc: t('gridDesc.allEvents') },
|
||||||
|
{ value: 'immich', icon: 'mdiCamera', label: t('providers.typeImmich'), desc: t('gridDesc.providerImmich') },
|
||||||
|
{ value: 'gitea', icon: 'mdiGit', label: t('providers.typeGitea'), desc: t('gridDesc.providerGitea') },
|
||||||
|
{ value: 'scheduler', icon: 'mdiClockOutline', label: t('providers.typeScheduler'), desc: t('gridDesc.providerScheduler') },
|
||||||
];
|
];
|
||||||
|
|
||||||
// --- Provider type ---
|
// --- Provider type ---
|
||||||
|
|
||||||
export const providerTypeItems = (): GridItem[] => [
|
export const providerTypeItems = (): GridItem[] => [
|
||||||
{ value: 'immich', icon: 'mdiCamera', label: t('providers.typeImmich') },
|
{ value: 'immich', icon: 'mdiCamera', label: t('providers.typeImmich'), desc: t('gridDesc.providerImmich') },
|
||||||
{ value: 'gitea', icon: 'mdiGit', label: t('providers.typeGitea') },
|
{ value: 'gitea', icon: 'mdiGit', label: t('providers.typeGitea'), desc: t('gridDesc.providerGitea') },
|
||||||
{ value: 'scheduler', icon: 'mdiClockOutline', label: t('providers.typeScheduler') },
|
{ value: 'scheduler', icon: 'mdiClockOutline', label: t('providers.typeScheduler'), desc: t('gridDesc.providerScheduler') },
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -755,6 +755,47 @@
|
|||||||
"allProviders": "All providers",
|
"allProviders": "All providers",
|
||||||
"noFilterResults": "No items match the current filter."
|
"noFilterResults": "No items match the current filter."
|
||||||
},
|
},
|
||||||
|
"gridDesc": {
|
||||||
|
"sortNone": "No sorting applied",
|
||||||
|
"sortDate": "Sort by creation date",
|
||||||
|
"sortRating": "Sort by star rating",
|
||||||
|
"sortName": "Sort by file name",
|
||||||
|
"sortRandom": "Randomize order",
|
||||||
|
"orderDesc": "Newest or highest first",
|
||||||
|
"orderAsc": "Oldest or lowest first",
|
||||||
|
"albumModePerAlbum": "One message per album",
|
||||||
|
"albumModeCombined": "All albums in one message",
|
||||||
|
"albumModeRandom": "Pick a random album",
|
||||||
|
"assetTypeAll": "Photos and videos",
|
||||||
|
"assetTypePhoto": "Photos only",
|
||||||
|
"assetTypeVideo": "Videos only",
|
||||||
|
"memorySourceAlbums": "Scan tracked albums for On This Day matches",
|
||||||
|
"memorySourceNative": "Use Immich native memories API",
|
||||||
|
"localeEn": "English interface",
|
||||||
|
"localeRu": "Russian interface",
|
||||||
|
"modeMedia": "Send actual photo/video files",
|
||||||
|
"modeText": "Send file names and links only",
|
||||||
|
"allEvents": "Show all event types",
|
||||||
|
"assetsAdded": "New files added to album",
|
||||||
|
"assetsRemoved": "Files removed from album",
|
||||||
|
"renamed": "Album was renamed",
|
||||||
|
"deleted": "Album was deleted",
|
||||||
|
"sharingChanged": "Album sharing toggled",
|
||||||
|
"newestFirst": "Most recent events on top",
|
||||||
|
"oldestFirst": "Oldest events on top",
|
||||||
|
"chatActionNone": "No indicator shown",
|
||||||
|
"chatActionTyping": "Show typing... bubble",
|
||||||
|
"chatActionUploadPhoto": "Show uploading photo...",
|
||||||
|
"chatActionUploadVideo": "Show uploading video...",
|
||||||
|
"chatActionUploadDoc": "Show uploading document...",
|
||||||
|
"chatActionRecordVideo": "Show recording video...",
|
||||||
|
"chatActionRecordVoice": "Show recording voice...",
|
||||||
|
"previewTelegram": "Preview with Telegram HTML format",
|
||||||
|
"previewWebhook": "Preview as plain text",
|
||||||
|
"providerImmich": "Self-hosted photo server",
|
||||||
|
"providerGitea": "Self-hosted Git service",
|
||||||
|
"providerScheduler": "Time-based scheduled messages"
|
||||||
|
},
|
||||||
"error": {
|
"error": {
|
||||||
"notFound": "Page not found",
|
"notFound": "Page not found",
|
||||||
"goHome": "Go home"
|
"goHome": "Go home"
|
||||||
|
|||||||
@@ -755,6 +755,47 @@
|
|||||||
"allProviders": "Все провайдеры",
|
"allProviders": "Все провайдеры",
|
||||||
"noFilterResults": "Нет элементов, соответствующих фильтру."
|
"noFilterResults": "Нет элементов, соответствующих фильтру."
|
||||||
},
|
},
|
||||||
|
"gridDesc": {
|
||||||
|
"sortNone": "Без сортировки",
|
||||||
|
"sortDate": "По дате создания",
|
||||||
|
"sortRating": "По рейтингу",
|
||||||
|
"sortName": "По имени файла",
|
||||||
|
"sortRandom": "Случайный порядок",
|
||||||
|
"orderDesc": "Сначала новые или высокие",
|
||||||
|
"orderAsc": "Сначала старые или низкие",
|
||||||
|
"albumModePerAlbum": "Отдельное сообщение для каждого альбома",
|
||||||
|
"albumModeCombined": "Все альбомы в одном сообщении",
|
||||||
|
"albumModeRandom": "Случайный альбом",
|
||||||
|
"assetTypeAll": "Фото и видео",
|
||||||
|
"assetTypePhoto": "Только фото",
|
||||||
|
"assetTypeVideo": "Только видео",
|
||||||
|
"memorySourceAlbums": "Поиск совпадений в отслеживаемых альбомах",
|
||||||
|
"memorySourceNative": "Использовать API воспоминаний Immich",
|
||||||
|
"localeEn": "Английский интерфейс",
|
||||||
|
"localeRu": "Русский интерфейс",
|
||||||
|
"modeMedia": "Отправка файлов фото/видео",
|
||||||
|
"modeText": "Только имена файлов и ссылки",
|
||||||
|
"allEvents": "Показать все типы событий",
|
||||||
|
"assetsAdded": "Новые файлы добавлены в альбом",
|
||||||
|
"assetsRemoved": "Файлы удалены из альбома",
|
||||||
|
"renamed": "Альбом переименован",
|
||||||
|
"deleted": "Альбом удалён",
|
||||||
|
"sharingChanged": "Изменён доступ к альбому",
|
||||||
|
"newestFirst": "Сначала новые события",
|
||||||
|
"oldestFirst": "Сначала старые события",
|
||||||
|
"chatActionNone": "Индикатор не показывается",
|
||||||
|
"chatActionTyping": "Показать «печатает...»",
|
||||||
|
"chatActionUploadPhoto": "Показать «отправляет фото...»",
|
||||||
|
"chatActionUploadVideo": "Показать «отправляет видео...»",
|
||||||
|
"chatActionUploadDoc": "Показать «отправляет документ...»",
|
||||||
|
"chatActionRecordVideo": "Показать «записывает видео...»",
|
||||||
|
"chatActionRecordVoice": "Показать «записывает голос...»",
|
||||||
|
"previewTelegram": "Предпросмотр в формате Telegram HTML",
|
||||||
|
"previewWebhook": "Предпросмотр как текст",
|
||||||
|
"providerImmich": "Фотосервер для самостоятельного размещения",
|
||||||
|
"providerGitea": "Git-сервер для самостоятельного размещения",
|
||||||
|
"providerScheduler": "Запланированные сообщения по расписанию"
|
||||||
|
},
|
||||||
"error": {
|
"error": {
|
||||||
"notFound": "Страница не найдена",
|
"notFound": "Страница не найдена",
|
||||||
"goHome": "На главную"
|
"goHome": "На главную"
|
||||||
|
|||||||
@@ -257,9 +257,9 @@
|
|||||||
placeholder={t('dashboard.searchEvents')}
|
placeholder={t('dashboard.searchEvents')}
|
||||||
class="w-full px-3 py-1.5 border border-[var(--color-border)] rounded-md text-sm bg-[var(--color-background)]" />
|
class="w-full px-3 py-1.5 border border-[var(--color-border)] rounded-md text-sm bg-[var(--color-background)]" />
|
||||||
</div>
|
</div>
|
||||||
<IconGridSelect items={eventTypeFilterItems()} bind:value={filterEventType} columns={3} compact />
|
<div class="w-40"><IconGridSelect items={eventTypeFilterItems()} bind:value={filterEventType} columns={3} compact /></div>
|
||||||
<IconGridSelect items={providerFilterItems} bind:value={filterProviderId} columns={2} compact />
|
<div class="w-40"><IconGridSelect items={providerFilterItems} bind:value={filterProviderId} columns={2} compact /></div>
|
||||||
<IconGridSelect items={sortFilterItems()} bind:value={filterSort} columns={2} compact />
|
<div class="w-36"><IconGridSelect items={sortFilterItems()} bind:value={filterSort} columns={2} compact /></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Chart (now inside Events section, affected by filters) -->
|
<!-- Chart (now inside Events section, affected by filters) -->
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
import IconButton from '$lib/components/IconButton.svelte';
|
import IconButton from '$lib/components/IconButton.svelte';
|
||||||
import CrossLink from '$lib/components/CrossLink.svelte';
|
import CrossLink from '$lib/components/CrossLink.svelte';
|
||||||
import IconGridSelect from '$lib/components/IconGridSelect.svelte';
|
import IconGridSelect from '$lib/components/IconGridSelect.svelte';
|
||||||
import { providerTypeItems, responseModeItems } from '$lib/grid-items';
|
import { providerTypeItems, providerTypeFilterItems, responseModeItems } from '$lib/grid-items';
|
||||||
import EntitySelect from '$lib/components/EntitySelect.svelte';
|
import EntitySelect from '$lib/components/EntitySelect.svelte';
|
||||||
import { snackSuccess, snackError } from '$lib/stores/snackbar.svelte';
|
import { snackSuccess, snackError } from '$lib/stores/snackbar.svelte';
|
||||||
import { highlightFromUrl } from '$lib/highlight';
|
import { highlightFromUrl } from '$lib/highlight';
|
||||||
@@ -202,7 +202,7 @@
|
|||||||
<div class="grid grid-cols-2 sm:grid-cols-3 gap-3">
|
<div class="grid grid-cols-2 sm:grid-cols-3 gap-3">
|
||||||
<div>
|
<div>
|
||||||
<label class="block text-xs mb-1">{t('commandConfig.responseMode')}</label>
|
<label class="block text-xs mb-1">{t('commandConfig.responseMode')}</label>
|
||||||
<IconGridSelect items={responseModeItems(t)} bind:value={form.response_mode} columns={2} />
|
<IconGridSelect items={responseModeItems(t)} bind:value={form.response_mode} columns={2} compact />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label class="block text-xs mb-1">{t('commandConfig.defaultCount')}</label>
|
<label class="block text-xs mb-1">{t('commandConfig.defaultCount')}</label>
|
||||||
@@ -239,12 +239,9 @@
|
|||||||
<div class="flex items-center gap-2 mb-3">
|
<div class="flex items-center gap-2 mb-3">
|
||||||
<input type="text" bind:value={filterText} placeholder={t('common.filterByName')}
|
<input type="text" bind:value={filterText} placeholder={t('common.filterByName')}
|
||||||
class="flex-1 px-3 py-1.5 border border-[var(--color-border)] rounded-md text-sm bg-[var(--color-background)]" />
|
class="flex-1 px-3 py-1.5 border border-[var(--color-border)] rounded-md text-sm bg-[var(--color-background)]" />
|
||||||
<select bind:value={filterType}
|
<div class="w-48">
|
||||||
class="px-3 py-1.5 border border-[var(--color-border)] rounded-md text-sm bg-[var(--color-background)]">
|
<IconGridSelect items={providerTypeFilterItems()} bind:value={filterType} columns={2} compact />
|
||||||
<option value="">{t('common.allTypes')}</option>
|
</div>
|
||||||
<option value="immich">Immich</option>
|
|
||||||
<option value="gitea">Gitea</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
import ConfirmModal from '$lib/components/ConfirmModal.svelte';
|
import ConfirmModal from '$lib/components/ConfirmModal.svelte';
|
||||||
import IconButton from '$lib/components/IconButton.svelte';
|
import IconButton from '$lib/components/IconButton.svelte';
|
||||||
import IconGridSelect from '$lib/components/IconGridSelect.svelte';
|
import IconGridSelect from '$lib/components/IconGridSelect.svelte';
|
||||||
import { providerTypeItems as providerTypeItemsFn } from '$lib/grid-items';
|
import { providerTypeItems as providerTypeItemsFn, providerTypeFilterItems } from '$lib/grid-items';
|
||||||
import Modal from '$lib/components/Modal.svelte';
|
import Modal from '$lib/components/Modal.svelte';
|
||||||
import JinjaEditor from '$lib/components/JinjaEditor.svelte';
|
import JinjaEditor from '$lib/components/JinjaEditor.svelte';
|
||||||
import { snackSuccess, snackError } from '$lib/stores/snackbar.svelte';
|
import { snackSuccess, snackError } from '$lib/stores/snackbar.svelte';
|
||||||
@@ -37,7 +37,13 @@
|
|||||||
|
|
||||||
const LOCALES = ['en', 'ru'] as const;
|
const LOCALES = ['en', 'ru'] as const;
|
||||||
|
|
||||||
let configs = $state<CmdTemplateConfig[]>([]);
|
let allCmdTplConfigs = $state<CmdTemplateConfig[]>([]);
|
||||||
|
let filterText = $state('');
|
||||||
|
let filterType = $state('');
|
||||||
|
let configs = $derived(allCmdTplConfigs.filter(c =>
|
||||||
|
(!filterText || c.name.toLowerCase().includes(filterText.toLowerCase())) &&
|
||||||
|
(!filterType || c.provider_type === filterType)
|
||||||
|
));
|
||||||
let loaded = $state(false);
|
let loaded = $state(false);
|
||||||
let showForm = $state(false);
|
let showForm = $state(false);
|
||||||
let editing = $state<number | null>(null);
|
let editing = $state<number | null>(null);
|
||||||
@@ -88,7 +94,7 @@
|
|||||||
api('/providers/capabilities'),
|
api('/providers/capabilities'),
|
||||||
api('/command-template-configs/variables'),
|
api('/command-template-configs/variables'),
|
||||||
]);
|
]);
|
||||||
configs = cfgs;
|
allCmdTplConfigs = cfgs;
|
||||||
allCapabilities = caps;
|
allCapabilities = caps;
|
||||||
varsRef = vars;
|
varsRef = vars;
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
@@ -325,10 +331,24 @@
|
|||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{#if configs.length === 0 && !showForm}
|
{#if !showForm && allCmdTplConfigs.length > 0}
|
||||||
|
<div class="flex items-center gap-2 mb-3">
|
||||||
|
<input type="text" bind:value={filterText} placeholder={t('common.filterByName')}
|
||||||
|
class="flex-1 px-3 py-1.5 border border-[var(--color-border)] rounded-md text-sm bg-[var(--color-background)]" />
|
||||||
|
<div class="w-48">
|
||||||
|
<IconGridSelect items={providerTypeFilterItems()} bind:value={filterType} columns={2} compact />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
{#if allCmdTplConfigs.length === 0 && !showForm}
|
||||||
<Card>
|
<Card>
|
||||||
<EmptyState icon="mdiConsoleLine" message={t('cmdTemplateConfig.noConfigs')} />
|
<EmptyState icon="mdiConsoleLine" message={t('cmdTemplateConfig.noConfigs')} />
|
||||||
</Card>
|
</Card>
|
||||||
|
{:else if configs.length === 0 && !showForm}
|
||||||
|
<Card>
|
||||||
|
<EmptyState icon="mdiFilterOff" message={t('common.noFilterResults')} />
|
||||||
|
</Card>
|
||||||
{:else}
|
{:else}
|
||||||
<div class="space-y-3 stagger-children">
|
<div class="space-y-3 stagger-children">
|
||||||
{#each configs as config}
|
{#each configs as config}
|
||||||
|
|||||||
@@ -222,13 +222,9 @@
|
|||||||
<div class="flex items-center gap-2 mb-3">
|
<div class="flex items-center gap-2 mb-3">
|
||||||
<input type="text" bind:value={filterText} placeholder={t('common.filterByName')}
|
<input type="text" bind:value={filterText} placeholder={t('common.filterByName')}
|
||||||
class="flex-1 px-3 py-1.5 border border-[var(--color-border)] rounded-md text-sm bg-[var(--color-background)]" />
|
class="flex-1 px-3 py-1.5 border border-[var(--color-border)] rounded-md text-sm bg-[var(--color-background)]" />
|
||||||
<select bind:value={filterProviderId}
|
<div class="w-48">
|
||||||
class="px-3 py-1.5 border border-[var(--color-border)] rounded-md text-sm bg-[var(--color-background)]">
|
<EntitySelect items={[{value: 0, label: t('common.allProviders'), icon: 'mdiFilterOff'}, ...providerItems]} bind:value={filterProviderId} placeholder={t('common.allProviders')} />
|
||||||
<option value={0}>{t('common.allProviders')}</option>
|
</div>
|
||||||
{#each providers as p}
|
|
||||||
<option value={p.id}>{p.name} ({p.type})</option>
|
|
||||||
{/each}
|
|
||||||
</select>
|
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
|||||||
@@ -377,13 +377,9 @@
|
|||||||
<div class="flex items-center gap-2 mb-3">
|
<div class="flex items-center gap-2 mb-3">
|
||||||
<input type="text" bind:value={filterText} placeholder={t('common.filterByName')}
|
<input type="text" bind:value={filterText} placeholder={t('common.filterByName')}
|
||||||
class="flex-1 px-3 py-1.5 border border-[var(--color-border)] rounded-md text-sm bg-[var(--color-background)]" />
|
class="flex-1 px-3 py-1.5 border border-[var(--color-border)] rounded-md text-sm bg-[var(--color-background)]" />
|
||||||
<select bind:value={filterProviderId}
|
<div class="w-48">
|
||||||
class="px-3 py-1.5 border border-[var(--color-border)] rounded-md text-sm bg-[var(--color-background)]">
|
<EntitySelect items={[{value: 0, label: t('common.allProviders'), icon: 'mdiFilterOff'}, ...providerItems]} bind:value={filterProviderId} placeholder={t('common.allProviders')} />
|
||||||
<option value={0}>{t('common.allProviders')}</option>
|
</div>
|
||||||
{#each providers as p}
|
|
||||||
<option value={p.id}>{p.name} ({p.type})</option>
|
|
||||||
{/each}
|
|
||||||
</select>
|
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,11 @@
|
|||||||
import { highlightFromUrl } from '$lib/highlight';
|
import { highlightFromUrl } from '$lib/highlight';
|
||||||
import type { ServiceProvider } from '$lib/types';
|
import type { ServiceProvider } from '$lib/types';
|
||||||
|
|
||||||
let providers = $derived(providersCache.items);
|
let allProviders = $derived(providersCache.items);
|
||||||
|
let filterText = $state('');
|
||||||
|
let providers = $derived(allProviders.filter(p =>
|
||||||
|
!filterText || p.name.toLowerCase().includes(filterText.toLowerCase()) || p.type.toLowerCase().includes(filterText.toLowerCase())
|
||||||
|
));
|
||||||
let showForm = $state(false);
|
let showForm = $state(false);
|
||||||
let editing = $state<number | null>(null);
|
let editing = $state<number | null>(null);
|
||||||
let form = $state({ name: 'Immich', type: 'immich', url: '', api_key: '', api_token: '', webhook_secret: '', external_domain: '', icon: '' });
|
let form = $state({ name: 'Immich', type: 'immich', url: '', api_key: '', api_token: '', webhook_secret: '', external_domain: '', icon: '' });
|
||||||
@@ -183,10 +187,21 @@
|
|||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{#if providers.length === 0 && !showForm}
|
{#if !showForm && allProviders.length > 0}
|
||||||
|
<div class="flex items-center gap-2 mb-3">
|
||||||
|
<input type="text" bind:value={filterText} placeholder={t('common.filterByName')}
|
||||||
|
class="flex-1 px-3 py-1.5 border border-[var(--color-border)] rounded-md text-sm bg-[var(--color-background)]" />
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
{#if allProviders.length === 0 && !showForm}
|
||||||
<Card>
|
<Card>
|
||||||
<EmptyState icon="mdiServer" message={t('providers.noProviders')} />
|
<EmptyState icon="mdiServer" message={t('providers.noProviders')} />
|
||||||
</Card>
|
</Card>
|
||||||
|
{:else if providers.length === 0 && !showForm}
|
||||||
|
<Card>
|
||||||
|
<EmptyState icon="mdiFilterOff" message={t('common.noFilterResults')} />
|
||||||
|
</Card>
|
||||||
{:else}
|
{:else}
|
||||||
<div class="space-y-3 stagger-children">
|
<div class="space-y-3 stagger-children">
|
||||||
{#each providers as provider}
|
{#each providers as provider}
|
||||||
|
|||||||
@@ -443,7 +443,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="col-span-2">
|
<div class="col-span-2">
|
||||||
<label class="block text-xs mb-1">{t('targets.chatAction')}</label>
|
<label class="block text-xs mb-1">{t('targets.chatAction')}</label>
|
||||||
<IconGridSelect items={chatActionItems()} bind:value={form.chat_action} columns={4} />
|
<IconGridSelect items={chatActionItems()} bind:value={form.chat_action} columns={4} compact />
|
||||||
</div>
|
</div>
|
||||||
<label class="flex items-center gap-2 text-sm col-span-2"><input type="checkbox" bind:checked={form.disable_url_preview} /> {t('targets.disableUrlPreview')}</label>
|
<label class="flex items-center gap-2 text-sm col-span-2"><input type="checkbox" bind:checked={form.disable_url_preview} /> {t('targets.disableUrlPreview')}</label>
|
||||||
<label class="flex items-center gap-2 text-sm col-span-2"><input type="checkbox" bind:checked={form.send_large_photos_as_documents} /> {t('targets.sendLargeAsDocuments')}</label>
|
<label class="flex items-center gap-2 text-sm col-span-2"><input type="checkbox" bind:checked={form.send_large_photos_as_documents} /> {t('targets.sendLargeAsDocuments')}</label>
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
import Hint from '$lib/components/Hint.svelte';
|
import Hint from '$lib/components/Hint.svelte';
|
||||||
import IconButton from '$lib/components/IconButton.svelte';
|
import IconButton from '$lib/components/IconButton.svelte';
|
||||||
import IconGridSelect from '$lib/components/IconGridSelect.svelte';
|
import IconGridSelect from '$lib/components/IconGridSelect.svelte';
|
||||||
import { providerTypeItems, previewTargetTypeItems } from '$lib/grid-items';
|
import { providerTypeItems, providerTypeFilterItems, previewTargetTypeItems } from '$lib/grid-items';
|
||||||
import Modal from '$lib/components/Modal.svelte';
|
import Modal from '$lib/components/Modal.svelte';
|
||||||
import JinjaEditor from '$lib/components/JinjaEditor.svelte';
|
import JinjaEditor from '$lib/components/JinjaEditor.svelte';
|
||||||
import { snackSuccess, snackError } from '$lib/stores/snackbar.svelte';
|
import { snackSuccess, snackError } from '$lib/stores/snackbar.svelte';
|
||||||
@@ -347,13 +347,9 @@
|
|||||||
<div class="flex items-center gap-2 mb-3">
|
<div class="flex items-center gap-2 mb-3">
|
||||||
<input type="text" bind:value={filterText} placeholder={t('common.filterByName')}
|
<input type="text" bind:value={filterText} placeholder={t('common.filterByName')}
|
||||||
class="flex-1 px-3 py-1.5 border border-[var(--color-border)] rounded-md text-sm bg-[var(--color-background)]" />
|
class="flex-1 px-3 py-1.5 border border-[var(--color-border)] rounded-md text-sm bg-[var(--color-background)]" />
|
||||||
<select bind:value={filterType}
|
<div class="w-48">
|
||||||
class="px-3 py-1.5 border border-[var(--color-border)] rounded-md text-sm bg-[var(--color-background)]">
|
<IconGridSelect items={providerTypeFilterItems()} bind:value={filterType} columns={2} compact />
|
||||||
<option value="">{t('common.allTypes')}</option>
|
</div>
|
||||||
<option value="immich">Immich</option>
|
|
||||||
<option value="gitea">Gitea</option>
|
|
||||||
<option value="scheduler">Scheduler</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
import Hint from '$lib/components/Hint.svelte';
|
import Hint from '$lib/components/Hint.svelte';
|
||||||
import IconButton from '$lib/components/IconButton.svelte';
|
import IconButton from '$lib/components/IconButton.svelte';
|
||||||
import IconGridSelect from '$lib/components/IconGridSelect.svelte';
|
import IconGridSelect from '$lib/components/IconGridSelect.svelte';
|
||||||
import { providerTypeItems, sortByItems, sortOrderItems, albumModeItems, assetTypeItems, memorySourceItems } from '$lib/grid-items';
|
import { providerTypeItems, providerTypeFilterItems, sortByItems, sortOrderItems, albumModeItems, assetTypeItems, memorySourceItems } from '$lib/grid-items';
|
||||||
import { snackSuccess, snackError } from '$lib/stores/snackbar.svelte';
|
import { snackSuccess, snackError } from '$lib/stores/snackbar.svelte';
|
||||||
import { highlightFromUrl } from '$lib/highlight';
|
import { highlightFromUrl } from '$lib/highlight';
|
||||||
import type { TrackingConfig } from '$lib/types';
|
import type { TrackingConfig } from '$lib/types';
|
||||||
@@ -163,11 +163,11 @@
|
|||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label class="block text-xs mb-1">{t('trackingConfig.sortBy')}</label>
|
<label class="block text-xs mb-1">{t('trackingConfig.sortBy')}</label>
|
||||||
<IconGridSelect items={sortByItems()} bind:value={form.assets_order_by} columns={2} />
|
<IconGridSelect items={sortByItems()} bind:value={form.assets_order_by} columns={2} compact />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label class="block text-xs mb-1">{t('trackingConfig.sortOrder')}</label>
|
<label class="block text-xs mb-1">{t('trackingConfig.sortOrder')}</label>
|
||||||
<IconGridSelect items={sortOrderItems()} bind:value={form.assets_order} columns={2} />
|
<IconGridSelect items={sortOrderItems()} bind:value={form.assets_order} columns={2} compact />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
@@ -195,10 +195,10 @@
|
|||||||
<div class="grid grid-cols-3 gap-3 mt-3">
|
<div class="grid grid-cols-3 gap-3 mt-3">
|
||||||
<div><label class="block text-xs mb-1">{t('trackingConfig.times')}<Hint text={t('hints.times')} /></label><input bind:value={form.scheduled_times} placeholder="09:00" class="w-full px-2 py-1 border border-[var(--color-border)] rounded-md text-sm bg-[var(--color-background)]" /></div>
|
<div><label class="block text-xs mb-1">{t('trackingConfig.times')}<Hint text={t('hints.times')} /></label><input bind:value={form.scheduled_times} placeholder="09:00" class="w-full px-2 py-1 border border-[var(--color-border)] rounded-md text-sm bg-[var(--color-background)]" /></div>
|
||||||
<div><label class="block text-xs mb-1">{t('trackingConfig.albumMode')}<Hint text={t('hints.albumMode')} /></label>
|
<div><label class="block text-xs mb-1">{t('trackingConfig.albumMode')}<Hint text={t('hints.albumMode')} /></label>
|
||||||
<IconGridSelect items={albumModeItems()} bind:value={form.scheduled_collection_mode} columns={3} /></div>
|
<IconGridSelect items={albumModeItems()} bind:value={form.scheduled_collection_mode} columns={3} compact /></div>
|
||||||
<div><label class="block text-xs mb-1">{t('trackingConfig.maxAssets')}<Hint text={t('hints.maxAssets')} /></label><input type="number" bind:value={form.scheduled_limit} min="1" max="100" class="w-full px-2 py-1 border border-[var(--color-border)] rounded-md text-sm bg-[var(--color-background)]" /></div>
|
<div><label class="block text-xs mb-1">{t('trackingConfig.maxAssets')}<Hint text={t('hints.maxAssets')} /></label><input type="number" bind:value={form.scheduled_limit} min="1" max="100" class="w-full px-2 py-1 border border-[var(--color-border)] rounded-md text-sm bg-[var(--color-background)]" /></div>
|
||||||
<div><label class="block text-xs mb-1">{t('trackingConfig.assetType')}</label>
|
<div><label class="block text-xs mb-1">{t('trackingConfig.assetType')}</label>
|
||||||
<IconGridSelect items={assetTypeItems()} bind:value={form.scheduled_asset_type} columns={3} /></div>
|
<IconGridSelect items={assetTypeItems()} bind:value={form.scheduled_asset_type} columns={3} compact /></div>
|
||||||
<div><label class="block text-xs mb-1">{t('trackingConfig.minRating')}<Hint text={t('hints.minRating')} /></label><input type="number" bind:value={form.scheduled_min_rating} min="0" max="5" class="w-full px-2 py-1 border border-[var(--color-border)] rounded-md text-sm bg-[var(--color-background)]" /></div>
|
<div><label class="block text-xs mb-1">{t('trackingConfig.minRating')}<Hint text={t('hints.minRating')} /></label><input type="number" bind:value={form.scheduled_min_rating} min="0" max="5" class="w-full px-2 py-1 border border-[var(--color-border)] rounded-md text-sm bg-[var(--color-background)]" /></div>
|
||||||
<label class="flex items-center gap-2 text-sm"><input type="checkbox" bind:checked={form.scheduled_favorite_only} /> {t('trackingConfig.favoritesOnly')}<Hint text={t('hints.favoritesOnly')} /></label>
|
<label class="flex items-center gap-2 text-sm"><input type="checkbox" bind:checked={form.scheduled_favorite_only} /> {t('trackingConfig.favoritesOnly')}<Hint text={t('hints.favoritesOnly')} /></label>
|
||||||
</div>
|
</div>
|
||||||
@@ -212,13 +212,13 @@
|
|||||||
{#if form.memory_enabled}
|
{#if form.memory_enabled}
|
||||||
<div class="grid grid-cols-3 gap-3 mt-3">
|
<div class="grid grid-cols-3 gap-3 mt-3">
|
||||||
<div><label class="block text-xs mb-1">{t('trackingConfig.memorySource')}<Hint text={t('hints.memorySource')} /></label>
|
<div><label class="block text-xs mb-1">{t('trackingConfig.memorySource')}<Hint text={t('hints.memorySource')} /></label>
|
||||||
<IconGridSelect items={memorySourceItems()} bind:value={form.memory_source} columns={2} /></div>
|
<IconGridSelect items={memorySourceItems()} bind:value={form.memory_source} columns={2} compact /></div>
|
||||||
<div><label class="block text-xs mb-1">{t('trackingConfig.times')}<Hint text={t('hints.times')} /></label><input bind:value={form.memory_times} placeholder="09:00" class="w-full px-2 py-1 border border-[var(--color-border)] rounded-md text-sm bg-[var(--color-background)]" /></div>
|
<div><label class="block text-xs mb-1">{t('trackingConfig.times')}<Hint text={t('hints.times')} /></label><input bind:value={form.memory_times} placeholder="09:00" class="w-full px-2 py-1 border border-[var(--color-border)] rounded-md text-sm bg-[var(--color-background)]" /></div>
|
||||||
<div><label class="block text-xs mb-1">{t('trackingConfig.albumMode')}<Hint text={t('hints.albumMode')} /></label>
|
<div><label class="block text-xs mb-1">{t('trackingConfig.albumMode')}<Hint text={t('hints.albumMode')} /></label>
|
||||||
<IconGridSelect items={albumModeItems()} bind:value={form.memory_collection_mode} columns={3} /></div>
|
<IconGridSelect items={albumModeItems()} bind:value={form.memory_collection_mode} columns={3} compact /></div>
|
||||||
<div><label class="block text-xs mb-1">{t('trackingConfig.maxAssets')}<Hint text={t('hints.maxAssets')} /></label><input type="number" bind:value={form.memory_limit} min="1" max="100" class="w-full px-2 py-1 border border-[var(--color-border)] rounded-md text-sm bg-[var(--color-background)]" /></div>
|
<div><label class="block text-xs mb-1">{t('trackingConfig.maxAssets')}<Hint text={t('hints.maxAssets')} /></label><input type="number" bind:value={form.memory_limit} min="1" max="100" class="w-full px-2 py-1 border border-[var(--color-border)] rounded-md text-sm bg-[var(--color-background)]" /></div>
|
||||||
<div><label class="block text-xs mb-1">{t('trackingConfig.assetType')}</label>
|
<div><label class="block text-xs mb-1">{t('trackingConfig.assetType')}</label>
|
||||||
<IconGridSelect items={assetTypeItems()} bind:value={form.memory_asset_type} columns={3} /></div>
|
<IconGridSelect items={assetTypeItems()} bind:value={form.memory_asset_type} columns={3} compact /></div>
|
||||||
<div><label class="block text-xs mb-1">{t('trackingConfig.minRating')}<Hint text={t('hints.minRating')} /></label><input type="number" bind:value={form.memory_min_rating} min="0" max="5" class="w-full px-2 py-1 border border-[var(--color-border)] rounded-md text-sm bg-[var(--color-background)]" /></div>
|
<div><label class="block text-xs mb-1">{t('trackingConfig.minRating')}<Hint text={t('hints.minRating')} /></label><input type="number" bind:value={form.memory_min_rating} min="0" max="5" class="w-full px-2 py-1 border border-[var(--color-border)] rounded-md text-sm bg-[var(--color-background)]" /></div>
|
||||||
<label class="flex items-center gap-2 text-sm"><input type="checkbox" bind:checked={form.memory_favorite_only} /> {t('trackingConfig.favoritesOnly')}<Hint text={t('hints.favoritesOnly')} /></label>
|
<label class="flex items-center gap-2 text-sm"><input type="checkbox" bind:checked={form.memory_favorite_only} /> {t('trackingConfig.favoritesOnly')}<Hint text={t('hints.favoritesOnly')} /></label>
|
||||||
</div>
|
</div>
|
||||||
@@ -238,13 +238,9 @@
|
|||||||
<div class="flex items-center gap-2 mb-3">
|
<div class="flex items-center gap-2 mb-3">
|
||||||
<input type="text" bind:value={filterText} placeholder={t('common.filterByName')}
|
<input type="text" bind:value={filterText} placeholder={t('common.filterByName')}
|
||||||
class="flex-1 px-3 py-1.5 border border-[var(--color-border)] rounded-md text-sm bg-[var(--color-background)]" />
|
class="flex-1 px-3 py-1.5 border border-[var(--color-border)] rounded-md text-sm bg-[var(--color-background)]" />
|
||||||
<select bind:value={filterType}
|
<div class="w-48">
|
||||||
class="px-3 py-1.5 border border-[var(--color-border)] rounded-md text-sm bg-[var(--color-background)]">
|
<IconGridSelect items={providerTypeFilterItems()} bind:value={filterType} columns={2} compact />
|
||||||
<option value="">{t('common.allTypes')}</option>
|
</div>
|
||||||
<option value="immich">Immich</option>
|
|
||||||
<option value="gitea">Gitea</option>
|
|
||||||
<option value="scheduler">Scheduler</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user