feat(customFormats): implement general queries and update related types and components

This commit is contained in:
Sam Chau
2025-12-31 03:31:59 +10:30
parent 445ebf1a39
commit 56cf061a4b
7 changed files with 131 additions and 8 deletions

View File

@@ -0,0 +1,43 @@
/**
* Custom format general queries
*/
import type { PCDCache } from '../../cache.ts';
import type { CustomFormatGeneral } from './types.ts';
/**
* Get general information for a single custom format
*/
export async function general(cache: PCDCache, formatId: number): Promise<CustomFormatGeneral | null> {
const db = cache.kb;
// Get the custom format
const format = await db
.selectFrom('custom_formats')
.select(['id', 'name', 'description', 'include_in_rename'])
.where('id', '=', formatId)
.executeTakeFirst();
if (!format) return null;
// Get tags for this format
const tags = await db
.selectFrom('custom_format_tags as cft')
.innerJoin('tags as t', 't.id', 'cft.tag_id')
.select(['t.id as tag_id', 't.name as tag_name', 't.created_at as tag_created_at'])
.where('cft.custom_format_id', '=', formatId)
.orderBy('t.name')
.execute();
return {
id: format.id,
name: format.name,
description: format.description || '',
include_in_rename: format.include_in_rename === 1,
tags: tags.map((tag) => ({
id: tag.tag_id,
name: tag.tag_name,
created_at: tag.tag_created_at
}))
};
}

View File

@@ -3,7 +3,7 @@
*/
// Export all types
export type { CustomFormatTableRow, ConditionRef, CustomFormatBasic, CustomFormatTest } from './types.ts';
export type { CustomFormatTableRow, ConditionRef, CustomFormatBasic, CustomFormatGeneral, CustomFormatTest } from './types.ts';
export type { CreateTestInput, CreateTestOptions } from './testCreate.ts';
export type { UpdateTestInput, UpdateTestOptions } from './testUpdate.ts';
export type { DeleteTestOptions } from './testDelete.ts';
@@ -12,6 +12,7 @@ export type { ConditionResult, EvaluationResult, ParsedInfo } from './evaluator.
// Export query functions (reads)
export { list } from './list.ts';
export { general } from './general.ts';
export { getById, listTests, getTestById } from './tests.ts';
export { getConditionsForEvaluation } from './conditions.ts';
export { evaluateCustomFormat, getParsedInfo } from './evaluator.ts';

View File

@@ -13,11 +13,16 @@ export async function getById(cache: PCDCache, formatId: number): Promise<Custom
const format = await db
.selectFrom('custom_formats')
.select(['id', 'name', 'description'])
.select(['id', 'name', 'description', 'include_in_rename'])
.where('id', '=', formatId)
.executeTakeFirst();
return format ?? null;
if (!format) return null;
return {
...format,
include_in_rename: format.include_in_rename === 1
};
}
/**

View File

@@ -27,6 +27,16 @@ export interface CustomFormatBasic {
id: number;
name: string;
description: string | null;
include_in_rename: boolean;
}
/** Custom format general information (for general tab) */
export interface CustomFormatGeneral {
id: number;
name: string;
description: string;
include_in_rename: boolean;
tags: Tag[];
}
/** Custom format test case */

View File

@@ -50,6 +50,7 @@ export interface CustomFormatsTable {
id: Generated<number>;
name: string;
description: string | null;
include_in_rename: Generated<number>;
created_at: Generated<string>;
updated_at: Generated<string>;
}

View File

@@ -29,8 +29,8 @@ export const load: ServerLoad = async ({ params }) => {
throw error(500, 'Database cache not available');
}
// Get custom format basic info
const format = await customFormatQueries.getById(cache, formatId);
// Load general information for the custom format
const format = await customFormatQueries.general(cache, formatId);
if (!format) {
throw error(404, 'Custom format not found');
}

View File

@@ -1,15 +1,78 @@
<script lang="ts">
import { Check } from 'lucide-svelte';
import FormInput from '$ui/form/FormInput.svelte';
import TagInput from '$ui/form/TagInput.svelte';
import IconCheckbox from '$ui/form/IconCheckbox.svelte';
import UnsavedChangesModal from '$ui/modal/UnsavedChangesModal.svelte';
import { useUnsavedChanges } from '$lib/client/utils/unsavedChanges.svelte';
import type { PageData } from './$types';
export let data: PageData;
const unsavedChanges = useUnsavedChanges();
let name = data.format.name;
let description = data.format.description;
let tags: string[] = data.format.tags.map((t) => t.name);
let includeInRename = data.format.include_in_rename;
// Mark as dirty when any field changes
$: if (
name !== data.format.name ||
description !== data.format.description ||
includeInRename !== data.format.include_in_rename
) {
unsavedChanges.markDirty();
}
</script>
<svelte:head>
<title>{data.format.name} - General - Profilarr</title>
</svelte:head>
<UnsavedChangesModal />
<div class="mt-6 space-y-6">
<p class="text-neutral-600 dark:text-neutral-400">
General tab placeholder - conditions and settings will go here.
</p>
<FormInput
label="Name"
description="The name of this custom format"
placeholder="Enter custom format name"
bind:value={name}
/>
<FormInput
label="Description"
description="Add any notes or details about this custom format's purpose and configuration. Use markdown to format your description."
placeholder=""
bind:value={description}
textarea={true}
/>
<div class="space-y-2">
<div class="block text-sm font-medium text-neutral-900 dark:text-neutral-100">Tags</div>
<p class="text-xs text-neutral-600 dark:text-neutral-400">
Add tags to organize and categorize this custom format.
</p>
<TagInput bind:tags />
<input type="hidden" name="tags" value={tags.length > 0 ? JSON.stringify(tags) : ''} />
</div>
<div class="space-y-2">
<div class="block text-sm font-medium text-neutral-900 dark:text-neutral-100">
Include In Rename
</div>
<p class="text-xs text-neutral-600 dark:text-neutral-400">
When enabled, this custom format's name will be included in the renamed filename.
</p>
<div class="flex items-center gap-2">
<IconCheckbox
icon={Check}
bind:checked={includeInRename}
on:click={() => (includeInRename = !includeInRename)}
/>
<span class="text-sm text-neutral-700 dark:text-neutral-300">
{includeInRename ? 'Enabled' : 'Disabled'}
</span>
</div>
</div>
</div>