mirror of
https://github.com/Dictionarry-Hub/profilarr.git
synced 2026-01-22 10:51:02 +01:00
refactor: update arr and databases pages with action bars, badges, and info modals
This commit is contained in:
@@ -4,6 +4,7 @@
|
||||
export let variant: 'accent' | 'neutral' | 'success' | 'warning' | 'danger' = 'accent';
|
||||
export let size: 'sm' | 'md' = 'sm';
|
||||
export let icon: ComponentType | null = null;
|
||||
export let mono: boolean = false;
|
||||
|
||||
const variantClasses: Record<typeof variant, string> = {
|
||||
accent: 'bg-accent-100 text-accent-800 dark:bg-accent-900 dark:text-accent-200',
|
||||
@@ -22,7 +23,7 @@
|
||||
</script>
|
||||
|
||||
<span
|
||||
class="inline-flex items-center gap-1 rounded font-medium {variantClasses[variant]} {sizeClasses[size]}"
|
||||
class="inline-flex items-center gap-1 rounded font-medium {variantClasses[variant]} {sizeClasses[size]} {mono ? 'font-mono' : ''}"
|
||||
>
|
||||
{#if icon}
|
||||
<svelte:component this={icon} size={iconSize} />
|
||||
|
||||
@@ -1,10 +1,17 @@
|
||||
<script lang="ts">
|
||||
import { Server, Plus, Trash2, Pencil, Check, X } from 'lucide-svelte';
|
||||
import { Server, Plus, Trash2, Pencil, Check, X, Info, ExternalLink } from 'lucide-svelte';
|
||||
import { goto } from '$app/navigation';
|
||||
import { enhance } from '$app/forms';
|
||||
import EmptyState from '$ui/state/EmptyState.svelte';
|
||||
import Table from '$ui/table/Table.svelte';
|
||||
import TableActionButton from '$ui/table/TableActionButton.svelte';
|
||||
import Badge from '$ui/badge/Badge.svelte';
|
||||
import Modal from '$ui/modal/Modal.svelte';
|
||||
import InfoModal from '$ui/modal/InfoModal.svelte';
|
||||
import ActionsBar from '$ui/actions/ActionsBar.svelte';
|
||||
import ActionButton from '$ui/actions/ActionButton.svelte';
|
||||
import SearchAction from '$ui/actions/SearchAction.svelte';
|
||||
import { createSearchStore } from '$stores/search';
|
||||
import { alertStore } from '$alerts/store';
|
||||
import type { PageData } from './$types';
|
||||
import type { Column } from '$ui/table/types';
|
||||
@@ -12,8 +19,15 @@
|
||||
|
||||
export let data: PageData;
|
||||
|
||||
// Search store
|
||||
const searchStore = createSearchStore();
|
||||
|
||||
// Filter instances based on search
|
||||
$: filteredInstances = searchStore.filterItems(data.instances, ['name', 'url', 'type']);
|
||||
|
||||
// Modal state
|
||||
let showDeleteModal = false;
|
||||
let showInfoModal = false;
|
||||
let selectedInstance: ArrInstance | null = null;
|
||||
let deleteFormElement: HTMLFormElement;
|
||||
|
||||
@@ -22,13 +36,11 @@
|
||||
return type.charAt(0).toUpperCase() + type.slice(1);
|
||||
}
|
||||
|
||||
// Get type badge color classes
|
||||
function getTypeBadgeClasses(type: string): string {
|
||||
const colors: Record<string, string> = {
|
||||
radarr: 'bg-amber-100 text-amber-800 dark:bg-amber-900/30 dark:text-amber-400',
|
||||
sonarr: 'bg-sky-100 text-sky-800 dark:bg-sky-900/30 dark:text-sky-400'
|
||||
};
|
||||
return colors[type] || 'bg-neutral-100 text-neutral-800 dark:bg-neutral-800 dark:text-neutral-200';
|
||||
// Get badge variant for arr type
|
||||
function getTypeVariant(type: string): 'warning' | 'accent' | 'neutral' {
|
||||
if (type === 'radarr') return 'warning';
|
||||
if (type === 'sonarr') return 'accent';
|
||||
return 'neutral';
|
||||
}
|
||||
|
||||
// Handle row click
|
||||
@@ -67,38 +79,24 @@
|
||||
/>
|
||||
{:else}
|
||||
<div class="space-y-6 p-8">
|
||||
<!-- Header -->
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<h1 class="text-3xl font-bold text-neutral-900 dark:text-neutral-50">Arr Instances</h1>
|
||||
<p class="mt-1 text-neutral-600 dark:text-neutral-400">
|
||||
Manage your Radarr and Sonarr instances
|
||||
</p>
|
||||
</div>
|
||||
<a
|
||||
href="/arr/new"
|
||||
class="inline-flex items-center gap-2 rounded-lg bg-blue-600 px-4 py-2 text-sm font-medium text-white transition-colors hover:bg-blue-700 dark:bg-blue-500 dark:hover:bg-blue-600"
|
||||
>
|
||||
<Plus size={16} />
|
||||
Add Instance
|
||||
</a>
|
||||
</div>
|
||||
<!-- Actions Bar -->
|
||||
<ActionsBar>
|
||||
<SearchAction {searchStore} placeholder="Search instances..." />
|
||||
<ActionButton icon={Plus} title="Add Instance" on:click={() => goto('/arr/new')} />
|
||||
<ActionButton icon={Info} title="Info" on:click={() => (showInfoModal = true)} />
|
||||
</ActionsBar>
|
||||
|
||||
<!-- Instance Table -->
|
||||
<Table {columns} data={data.instances} hoverable={true} onRowClick={handleRowClick}>
|
||||
<Table {columns} data={filteredInstances} hoverable={true} onRowClick={handleRowClick}>
|
||||
<svelte:fragment slot="cell" let:row let:column>
|
||||
{#if column.key === 'name'}
|
||||
<div class="font-medium text-neutral-900 dark:text-neutral-50">
|
||||
{row.name}
|
||||
</div>
|
||||
{:else if column.key === 'type'}
|
||||
<span class="inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium {getTypeBadgeClasses(row.type)}">
|
||||
{formatType(row.type)}
|
||||
</span>
|
||||
<Badge variant={getTypeVariant(row.type)} mono>{formatType(row.type)}</Badge>
|
||||
{:else if column.key === 'url'}
|
||||
<code class="rounded bg-neutral-100 px-2 py-1 font-mono text-xs text-neutral-700 dark:bg-neutral-800 dark:text-neutral-300">
|
||||
{row.url}
|
||||
</code>
|
||||
<Badge variant="neutral" mono>{row.url}</Badge>
|
||||
{:else if column.key === 'enabled'}
|
||||
<div class="flex justify-center">
|
||||
{#if row.enabled}
|
||||
@@ -115,26 +113,19 @@
|
||||
</svelte:fragment>
|
||||
|
||||
<svelte:fragment slot="actions" let:row>
|
||||
<div class="flex items-center justify-end gap-2">
|
||||
<!-- Edit Button -->
|
||||
<a
|
||||
href="/arr/{row.id}/edit"
|
||||
on:click={(e) => e.stopPropagation()}
|
||||
class="inline-flex h-7 w-7 cursor-pointer items-center justify-center rounded-lg border border-neutral-300 bg-white text-neutral-600 transition-colors hover:bg-neutral-50 dark:border-neutral-700 dark:bg-neutral-800 dark:text-neutral-400 dark:hover:bg-neutral-700"
|
||||
title="Edit instance"
|
||||
>
|
||||
<Pencil size={14} />
|
||||
<div class="flex items-center justify-end gap-1">
|
||||
<a href="/arr/{row.id}/edit" on:click={(e) => e.stopPropagation()}>
|
||||
<TableActionButton icon={Pencil} title="Edit instance" />
|
||||
</a>
|
||||
|
||||
<!-- Delete Button -->
|
||||
<button
|
||||
type="button"
|
||||
on:click={(e) => handleDeleteClick(e, row)}
|
||||
class="inline-flex h-7 w-7 cursor-pointer items-center justify-center rounded-lg border border-neutral-300 bg-white text-red-600 transition-colors hover:bg-neutral-50 dark:border-neutral-700 dark:bg-neutral-800 dark:text-red-400 dark:hover:bg-neutral-700"
|
||||
<a href={row.url} target="_blank" rel="noopener noreferrer" on:click={(e) => e.stopPropagation()}>
|
||||
<TableActionButton icon={ExternalLink} title="Open in {formatType(row.type)}" />
|
||||
</a>
|
||||
<TableActionButton
|
||||
icon={Trash2}
|
||||
title="Delete instance"
|
||||
>
|
||||
<Trash2 size={14} />
|
||||
</button>
|
||||
variant="danger"
|
||||
on:click={(e) => handleDeleteClick(e, row)}
|
||||
/>
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
</Table>
|
||||
@@ -184,3 +175,41 @@
|
||||
>
|
||||
<input type="hidden" name="id" value={selectedInstance?.id || ''} />
|
||||
</form>
|
||||
|
||||
<!-- Info Modal -->
|
||||
<InfoModal bind:open={showInfoModal} header="Arr Instances">
|
||||
<div class="space-y-4 text-sm text-neutral-600 dark:text-neutral-400">
|
||||
<div>
|
||||
<div class="font-medium text-neutral-900 dark:text-neutral-100">What are Arr Instances?</div>
|
||||
<div class="mt-1">
|
||||
Arr instances are your Radarr and Sonarr applications. Profilarr connects to these
|
||||
instances to sync quality profiles, custom formats, and other configurations.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="font-medium text-neutral-900 dark:text-neutral-100">Adding an Instance</div>
|
||||
<div class="mt-1">
|
||||
To add an instance, you'll need the URL and API key from your Radarr or Sonarr
|
||||
application. You can find the API key in Settings → General → Security.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="font-medium text-neutral-900 dark:text-neutral-100">Syncing</div>
|
||||
<div class="mt-1">
|
||||
Once connected, you can configure sync settings to push profiles and formats from your
|
||||
linked databases to each instance. Sync can be triggered manually, on a schedule, or
|
||||
automatically when changes are detected.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="font-medium text-neutral-900 dark:text-neutral-100">Enabled/Disabled</div>
|
||||
<div class="mt-1">
|
||||
Disabled instances are excluded from sync operations but remain configured. This is
|
||||
useful for temporarily pausing sync without removing the instance.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</InfoModal>
|
||||
|
||||
@@ -307,6 +307,7 @@
|
||||
{activeFilters}
|
||||
uniqueQualities={loading ? [] : uniqueQualities}
|
||||
uniqueProfiles={loading ? [] : uniqueProfiles}
|
||||
instanceName={data.instance.name}
|
||||
onToggleColumn={toggleColumn}
|
||||
onToggleFilter={toggleFilter}
|
||||
onRefresh={handleRefresh}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
import ActionButton from '$ui/actions/ActionButton.svelte';
|
||||
import Dropdown from '$ui/dropdown/Dropdown.svelte';
|
||||
import IconCheckbox from '$ui/form/IconCheckbox.svelte';
|
||||
import Modal from '$ui/modal/Modal.svelte';
|
||||
import { type SearchStore } from '$stores/search';
|
||||
|
||||
type FilterOperator = 'eq' | 'neq';
|
||||
@@ -24,6 +25,7 @@
|
||||
export let activeFilters: ActiveFilter[];
|
||||
export let uniqueQualities: string[];
|
||||
export let uniqueProfiles: string[];
|
||||
export let instanceName: string = '';
|
||||
|
||||
export let onToggleColumn: (key: string) => void;
|
||||
export let onToggleFilter: (field: FilterField, operator: FilterOperator, value: string | number | boolean, label: string) => void;
|
||||
@@ -32,6 +34,8 @@
|
||||
export let onEdit: () => void;
|
||||
export let onDelete: () => void;
|
||||
export let onInfo: () => void;
|
||||
|
||||
let showDeleteModal = false;
|
||||
</script>
|
||||
|
||||
<ActionsBar>
|
||||
@@ -173,7 +177,7 @@
|
||||
<Dropdown position={dropdownPosition} {open} minWidth="10rem">
|
||||
<button
|
||||
type="button"
|
||||
on:click={onDelete}
|
||||
on:click={() => (showDeleteModal = true)}
|
||||
class="w-full px-4 py-2 text-left text-sm text-neutral-700 hover:bg-neutral-100 dark:text-neutral-300 dark:hover:bg-neutral-700 rounded-lg"
|
||||
>
|
||||
Delete instance
|
||||
@@ -182,3 +186,17 @@
|
||||
</svelte:fragment>
|
||||
</ActionButton>
|
||||
</ActionsBar>
|
||||
|
||||
<Modal
|
||||
open={showDeleteModal}
|
||||
header="Delete Instance"
|
||||
bodyMessage={`Are you sure you want to delete "${instanceName}"? This action cannot be undone.`}
|
||||
confirmText="Delete"
|
||||
cancelText="Cancel"
|
||||
confirmDanger={true}
|
||||
on:confirm={() => {
|
||||
showDeleteModal = false;
|
||||
onDelete();
|
||||
}}
|
||||
on:cancel={() => (showDeleteModal = false)}
|
||||
/>
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import { Check, ExternalLink, CircleAlert } from 'lucide-svelte';
|
||||
import Score from '$ui/arr/Score.svelte';
|
||||
import CustomFormatBadge from '$ui/arr/CustomFormatBadge.svelte';
|
||||
import Badge from '$ui/badge/Badge.svelte';
|
||||
import type { RadarrLibraryItem } from '$utils/arr/types.ts';
|
||||
import type { Column } from '$ui/table/types';
|
||||
|
||||
@@ -34,14 +35,13 @@
|
||||
</div>
|
||||
{:else if column.key === 'qualityProfileName'}
|
||||
<div class="relative group inline-flex">
|
||||
<span
|
||||
class="inline-flex items-center gap-1 rounded-full px-2.5 py-0.5 text-xs font-medium {row.isProfilarrProfile ? 'bg-blue-100 text-blue-800 dark:bg-blue-900/30 dark:text-blue-400' : 'bg-amber-100 text-amber-800 dark:bg-amber-900/30 dark:text-amber-400'}"
|
||||
<Badge
|
||||
variant={row.isProfilarrProfile ? 'accent' : 'warning'}
|
||||
icon={row.isProfilarrProfile ? null : CircleAlert}
|
||||
mono
|
||||
>
|
||||
{#if !row.isProfilarrProfile}
|
||||
<CircleAlert size={12} />
|
||||
{/if}
|
||||
{row.qualityProfileName}
|
||||
</span>
|
||||
</Badge>
|
||||
{#if !row.isProfilarrProfile}
|
||||
<div class="absolute left-1/2 -translate-x-1/2 bottom-full mb-1 px-2 py-1 text-xs text-white bg-neutral-800 dark:bg-neutral-700 rounded whitespace-nowrap opacity-0 group-hover:opacity-100 pointer-events-none z-10">
|
||||
Not managed by Profilarr
|
||||
@@ -49,9 +49,7 @@
|
||||
{/if}
|
||||
</div>
|
||||
{:else if column.key === 'qualityName'}
|
||||
<code class="rounded bg-neutral-100 px-2 py-1 font-mono text-xs text-neutral-700 dark:bg-neutral-800 dark:text-neutral-300">
|
||||
{row.qualityName ?? 'N/A'}
|
||||
</code>
|
||||
<Badge variant="neutral" mono>{row.qualityName ?? 'N/A'}</Badge>
|
||||
{:else if column.key === 'customFormatScore'}
|
||||
<div class="text-right">
|
||||
<Score score={row.customFormatScore} showSign={false} colored={false} />
|
||||
@@ -76,13 +74,9 @@
|
||||
{/if}
|
||||
</div>
|
||||
{:else if column.key === 'popularity'}
|
||||
<span class="font-mono text-sm text-neutral-600 dark:text-neutral-400">
|
||||
{row.popularity?.toFixed(1) ?? '-'}
|
||||
</span>
|
||||
<Badge variant="neutral" mono>{row.popularity?.toFixed(1) ?? '-'}</Badge>
|
||||
{:else if column.key === 'dateAdded'}
|
||||
<span class="text-sm text-neutral-600 dark:text-neutral-400">
|
||||
{formatDate(row.dateAdded)}
|
||||
</span>
|
||||
<Badge variant="neutral" mono>{formatDate(row.dateAdded)}</Badge>
|
||||
{:else if column.key === 'actions'}
|
||||
<div class="flex items-center justify-center">
|
||||
{#if row.tmdbId}
|
||||
|
||||
@@ -1,10 +1,17 @@
|
||||
<script lang="ts">
|
||||
import { Database, Plus, Lock, Code, Trash2, Pencil, ExternalLink, ChevronRight } from 'lucide-svelte';
|
||||
import { Database, Plus, Lock, Code, Trash2, Pencil, ExternalLink, ChevronRight, Info } from 'lucide-svelte';
|
||||
import { goto } from '$app/navigation';
|
||||
import { enhance } from '$app/forms';
|
||||
import EmptyState from '$ui/state/EmptyState.svelte';
|
||||
import Table from '$ui/table/Table.svelte';
|
||||
import TableActionButton from '$ui/table/TableActionButton.svelte';
|
||||
import Badge from '$ui/badge/Badge.svelte';
|
||||
import Modal from '$ui/modal/Modal.svelte';
|
||||
import InfoModal from '$ui/modal/InfoModal.svelte';
|
||||
import ActionsBar from '$ui/actions/ActionsBar.svelte';
|
||||
import ActionButton from '$ui/actions/ActionButton.svelte';
|
||||
import SearchAction from '$ui/actions/SearchAction.svelte';
|
||||
import { createSearchStore } from '$stores/search';
|
||||
import { alertStore } from '$alerts/store';
|
||||
import type { PageData } from './$types';
|
||||
import type { Column } from '$ui/table/types';
|
||||
@@ -12,8 +19,15 @@
|
||||
|
||||
export let data: PageData;
|
||||
|
||||
// Search store
|
||||
const searchStore = createSearchStore();
|
||||
|
||||
// Filter databases based on search
|
||||
$: filteredDatabases = searchStore.filterItems(data.databases, ['name', 'repository_url']);
|
||||
|
||||
// Modal state
|
||||
let showUnlinkModal = false;
|
||||
let showInfoModal = false;
|
||||
let selectedDatabase: DatabaseInstance | null = null;
|
||||
let unlinkFormElement: HTMLFormElement;
|
||||
|
||||
@@ -85,25 +99,15 @@
|
||||
/>
|
||||
{:else}
|
||||
<div class="space-y-6 p-8">
|
||||
<!-- Header -->
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<h1 class="text-3xl font-bold text-neutral-900 dark:text-neutral-50">Databases</h1>
|
||||
<p class="mt-1 text-neutral-600 dark:text-neutral-400">
|
||||
Manage your linked Profilarr Compliant Databases
|
||||
</p>
|
||||
</div>
|
||||
<a
|
||||
href="/databases/new"
|
||||
class="inline-flex items-center gap-2 rounded-lg bg-accent-600 px-4 py-2 text-sm font-medium text-white transition-colors hover:bg-accent-700 dark:bg-accent-500 dark:hover:bg-accent-600"
|
||||
>
|
||||
<Plus size={16} />
|
||||
Link Database
|
||||
</a>
|
||||
</div>
|
||||
<!-- Actions Bar -->
|
||||
<ActionsBar>
|
||||
<SearchAction {searchStore} placeholder="Search databases..." />
|
||||
<ActionButton icon={Plus} title="Link Database" on:click={() => goto('/databases/new')} />
|
||||
<ActionButton icon={Info} title="Info" on:click={() => (showInfoModal = true)} />
|
||||
</ActionsBar>
|
||||
|
||||
<!-- Database Table -->
|
||||
<Table {columns} data={data.databases} hoverable={true}>
|
||||
<Table {columns} data={filteredDatabases} hoverable={true}>
|
||||
<svelte:fragment slot="cell" let:row let:column>
|
||||
<div on:click={() => handleRowClick(row)} role="button" tabindex="0" on:keydown={(e) => e.key === 'Enter' && handleRowClick(row)} class="cursor-pointer">
|
||||
{#if column.key === 'name'}
|
||||
@@ -124,78 +128,37 @@
|
||||
{row.name}
|
||||
</div>
|
||||
{#if row.is_private}
|
||||
<span class="inline-flex items-center gap-1 rounded-full bg-yellow-100 px-2 py-0.5 text-xs font-medium text-yellow-800 dark:bg-yellow-900/30 dark:text-yellow-400">
|
||||
<Lock size={10} />
|
||||
Private
|
||||
</span>
|
||||
<Badge variant="warning" icon={Lock} mono>Private</Badge>
|
||||
{/if}
|
||||
{#if row.personal_access_token}
|
||||
<span class="inline-flex items-center gap-1 rounded-full bg-green-100 px-2 py-0.5 text-xs font-medium text-green-800 dark:bg-green-900/30 dark:text-green-400">
|
||||
<Code size={10} />
|
||||
Dev
|
||||
</span>
|
||||
<Badge variant="success" icon={Code} mono>Dev</Badge>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
{:else if column.key === 'repository_url'}
|
||||
<code class="rounded bg-neutral-100 px-2 py-1 font-mono text-xs text-neutral-700 dark:bg-neutral-800 dark:text-neutral-300">
|
||||
{row.repository_url.replace('https://github.com/', '')}
|
||||
</code>
|
||||
<Badge variant="neutral" mono>{row.repository_url.replace('https://github.com/', '')}</Badge>
|
||||
{:else if column.key === 'sync_strategy'}
|
||||
<code class="rounded bg-neutral-100 px-2 py-1 font-mono text-xs text-neutral-700 dark:bg-neutral-800 dark:text-neutral-300">
|
||||
{formatSyncStrategy(row.sync_strategy)}
|
||||
</code>
|
||||
<Badge variant="neutral" mono>{formatSyncStrategy(row.sync_strategy)}</Badge>
|
||||
{:else if column.key === 'last_synced_at'}
|
||||
<code class="rounded bg-neutral-100 px-2 py-1 font-mono text-xs text-neutral-700 dark:bg-neutral-800 dark:text-neutral-300">
|
||||
{formatLastSynced(row.last_synced_at)}
|
||||
</code>
|
||||
<Badge variant="neutral" mono>{formatLastSynced(row.last_synced_at)}</Badge>
|
||||
{/if}
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
|
||||
<svelte:fragment slot="actions" let:row>
|
||||
<div class="flex items-center justify-end gap-2">
|
||||
<!-- GitHub Link Button -->
|
||||
<a
|
||||
href={row.repository_url}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
on:click={(e) => e.stopPropagation()}
|
||||
class="inline-flex h-7 w-7 cursor-pointer items-center justify-center rounded-lg border border-neutral-300 bg-white text-accent-600 transition-colors hover:bg-neutral-50 dark:border-neutral-700 dark:bg-neutral-800 dark:text-accent-400 dark:hover:bg-neutral-700"
|
||||
title="View on GitHub"
|
||||
>
|
||||
<ExternalLink size={14} />
|
||||
<div class="flex items-center justify-end gap-1">
|
||||
<a href="/databases/{row.id}/edit" on:click={(e) => e.stopPropagation()}>
|
||||
<TableActionButton icon={Pencil} title="Edit database" />
|
||||
</a>
|
||||
|
||||
<!-- Edit Button -->
|
||||
<a
|
||||
href="/databases/{row.id}/edit"
|
||||
on:click={(e) => e.stopPropagation()}
|
||||
class="inline-flex h-7 w-7 cursor-pointer items-center justify-center rounded-lg border border-neutral-300 bg-white text-neutral-600 transition-colors hover:bg-neutral-50 dark:border-neutral-700 dark:bg-neutral-800 dark:text-neutral-400 dark:hover:bg-neutral-700"
|
||||
title="Edit database"
|
||||
>
|
||||
<Pencil size={14} />
|
||||
<a href={row.repository_url} target="_blank" rel="noopener noreferrer" on:click={(e) => e.stopPropagation()}>
|
||||
<TableActionButton icon={ExternalLink} title="View on GitHub" />
|
||||
</a>
|
||||
|
||||
<!-- Unlink Button -->
|
||||
<button
|
||||
type="button"
|
||||
on:click={(e) => handleUnlinkClick(e, row)}
|
||||
class="inline-flex h-7 w-7 cursor-pointer items-center justify-center rounded-lg border border-neutral-300 bg-white text-red-600 transition-colors hover:bg-neutral-50 dark:border-neutral-700 dark:bg-neutral-800 dark:text-red-400 dark:hover:bg-neutral-700"
|
||||
<TableActionButton
|
||||
icon={Trash2}
|
||||
title="Unlink database"
|
||||
>
|
||||
<Trash2 size={14} />
|
||||
</button>
|
||||
|
||||
<!-- View Button -->
|
||||
<button
|
||||
type="button"
|
||||
on:click={() => handleRowClick(row)}
|
||||
class="inline-flex h-7 w-7 cursor-pointer items-center justify-center rounded-lg border border-neutral-300 bg-white text-neutral-600 transition-colors hover:bg-neutral-50 dark:border-neutral-700 dark:bg-neutral-800 dark:text-neutral-400 dark:hover:bg-neutral-700"
|
||||
title={row.personal_access_token ? "View changes" : "View commits"}
|
||||
>
|
||||
<ChevronRight size={14} />
|
||||
</button>
|
||||
variant="danger"
|
||||
on:click={(e) => handleUnlinkClick(e, row)}
|
||||
/>
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
</Table>
|
||||
@@ -245,3 +208,44 @@
|
||||
>
|
||||
<input type="hidden" name="id" value={selectedDatabase?.id || ''} />
|
||||
</form>
|
||||
|
||||
<!-- Info Modal -->
|
||||
<InfoModal bind:open={showInfoModal} header="Databases">
|
||||
<div class="space-y-4 text-sm text-neutral-600 dark:text-neutral-400">
|
||||
<div>
|
||||
<div class="font-medium text-neutral-900 dark:text-neutral-100">What are Databases?</div>
|
||||
<div class="mt-1">
|
||||
Databases are Profilarr Compliant Database (PCD) repositories containing quality profiles,
|
||||
custom formats, and other configurations. Link a database to import and sync configurations
|
||||
to your Arr instances.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="font-medium text-neutral-900 dark:text-neutral-100">Private & Dev Badges</div>
|
||||
<div class="mt-1">
|
||||
<strong>Private</strong> indicates the repository requires authentication.
|
||||
<strong>Dev</strong> means you have a personal access token configured, allowing you to
|
||||
push changes back to the repository.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="font-medium text-neutral-900 dark:text-neutral-100">Sync Strategy</div>
|
||||
<div class="mt-1">
|
||||
Controls how often Profilarr checks for updates from the remote repository. Set to
|
||||
"Manual" to only sync when you explicitly trigger it, or choose an interval for
|
||||
automatic updates.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="font-medium text-neutral-900 dark:text-neutral-100">Unlinking</div>
|
||||
<div class="mt-1">
|
||||
Unlinking a database removes all local data associated with it. Your Arr instances
|
||||
will keep any configurations that were already synced, but you won't be able to
|
||||
sync updates until you re-link the database.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</InfoModal>
|
||||
|
||||
@@ -98,6 +98,7 @@
|
||||
},
|
||||
{
|
||||
name: 'Seraphys',
|
||||
remark: 'Your sync broke? But the conditions are in order now!',
|
||||
tags: ['Dictionarry Database Lead', 'Sexy God']
|
||||
}
|
||||
];
|
||||
@@ -315,8 +316,15 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Greetz -->
|
||||
<div class="mt-6 text-center">
|
||||
<p class="text-sm text-neutral-500 dark:text-neutral-400">
|
||||
<span class="font-medium">Greetz:</span> Ba11in0nABudget, SFusion, some_guy, delavicci
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Dedication -->
|
||||
<div class="mt-8 text-center">
|
||||
<div class="mt-4 text-center">
|
||||
<p class="text-sm text-neutral-500 italic dark:text-neutral-400">
|
||||
This project is dedicated to Faiza, for helping me find my heart.
|
||||
</p>
|
||||
|
||||
Reference in New Issue
Block a user