refactor(dates): type DocumentMultiSelect options without double-cast
The search results were mapped to a partial object then forced with `as unknown as Document[]`. DocumentListItem already carries every field the picker reads (id, title, documentDate, metaDatePrecision REQUIRED, metaDateEnd), so introduce a DocumentOption Pick type and drop the double-cast — the mapped objects are now honestly typed. Refs #666 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -5,11 +5,20 @@ import { clickOutside } from '$lib/shared/actions/clickOutside';
|
||||
import { formatDocumentDate, type DatePrecision } from '$lib/shared/utils/documentDate';
|
||||
import { getLocale } from '$lib/paraglide/runtime.js';
|
||||
|
||||
type Document = components['schemas']['Document'];
|
||||
type DocumentListItem = components['schemas']['DocumentListItem'];
|
||||
|
||||
/**
|
||||
* Exactly the fields this picker reads — id for selection/dedup, the rest for
|
||||
* the honest date label. A full `Document` and a `DocumentListItem` are both
|
||||
* structurally assignable, so the search results need no cast.
|
||||
*/
|
||||
type DocumentOption = Pick<
|
||||
DocumentListItem,
|
||||
'id' | 'title' | 'documentDate' | 'metaDatePrecision' | 'metaDateEnd'
|
||||
>;
|
||||
|
||||
interface Props {
|
||||
selectedDocuments?: Document[];
|
||||
selectedDocuments?: DocumentOption[];
|
||||
placeholder?: string;
|
||||
hiddenInputName?: string;
|
||||
}
|
||||
@@ -21,7 +30,7 @@ let {
|
||||
}: Props = $props();
|
||||
|
||||
let searchTerm = $state('');
|
||||
let results: Document[] = $state([]);
|
||||
let results: DocumentOption[] = $state([]);
|
||||
let showDropdown = $state(false);
|
||||
let loading = $state(false);
|
||||
let debounceTimer: ReturnType<typeof setTimeout>;
|
||||
@@ -47,13 +56,13 @@ function handleInput() {
|
||||
const res = await fetch(`/api/documents/search?q=${encodeURIComponent(searchTerm)}&size=10`);
|
||||
if (res.ok) {
|
||||
const body: { items: DocumentListItem[] } = await res.json();
|
||||
const docs = body.items.map((it) => ({
|
||||
const docs: DocumentOption[] = body.items.map((it) => ({
|
||||
id: it.id,
|
||||
title: it.title,
|
||||
documentDate: it.documentDate,
|
||||
metaDatePrecision: it.metaDatePrecision,
|
||||
metaDateEnd: it.metaDateEnd
|
||||
})) as unknown as Document[];
|
||||
}));
|
||||
results = docs.filter((d) => !selectedDocuments.some((s) => s.id === d.id));
|
||||
}
|
||||
} catch {
|
||||
@@ -64,7 +73,7 @@ function handleInput() {
|
||||
}, 300);
|
||||
}
|
||||
|
||||
function selectDocument(doc: Document) {
|
||||
function selectDocument(doc: DocumentOption) {
|
||||
selectedDocuments = [...selectedDocuments, doc];
|
||||
searchTerm = '';
|
||||
showDropdown = false;
|
||||
@@ -75,10 +84,15 @@ function removeDocument(id: string | undefined) {
|
||||
selectedDocuments = selectedDocuments.filter((d) => d.id !== id);
|
||||
}
|
||||
|
||||
function formatDocLabel(doc: Document): string {
|
||||
function formatDocLabel(doc: DocumentOption): string {
|
||||
if (!doc.documentDate) return doc.title;
|
||||
const precision = (doc.metaDatePrecision as DatePrecision | undefined) ?? 'DAY';
|
||||
const label = formatDocumentDate(doc.documentDate, precision, doc.metaDateEnd, null, getLocale());
|
||||
const label = formatDocumentDate(
|
||||
doc.documentDate,
|
||||
doc.metaDatePrecision as DatePrecision,
|
||||
doc.metaDateEnd,
|
||||
null,
|
||||
getLocale()
|
||||
);
|
||||
return `${doc.title} · ${label}`;
|
||||
}
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user