76 lines
2.4 KiB
TypeScript
76 lines
2.4 KiB
TypeScript
import type { H3Event } from 'h3'
|
|
|
|
interface LookupValue {
|
|
value: string | null
|
|
}
|
|
|
|
// title, date and author_or_director are lookups through the `books` link
|
|
export interface ReviewRow {
|
|
id: number
|
|
books: LookupValue[]
|
|
title: LookupValue[]
|
|
date: LookupValue[]
|
|
author_or_director: LookupValue[]
|
|
review: string | null
|
|
medium: SelectOption | null
|
|
}
|
|
|
|
interface SelectOption {
|
|
id: number
|
|
value: string
|
|
}
|
|
|
|
interface Field {
|
|
name: string
|
|
select_options?: SelectOption[]
|
|
link_row_table_id?: number
|
|
}
|
|
|
|
const FIELD_CACHE_MS = 10 * 60_000
|
|
|
|
let reviewFields: { fields: Field[], fetchedAt: number } | null = null
|
|
|
|
async function fields(event: H3Event): Promise<Field[]> {
|
|
if (!reviewFields || Date.now() - reviewFields.fetchedAt > FIELD_CACHE_MS) {
|
|
reviewFields = { fields: await listFields<Field>(event, diaryTable(event, 'reviews')), fetchedAt: Date.now() }
|
|
}
|
|
return reviewFields.fields
|
|
}
|
|
|
|
function toMedium(value: string | undefined): Medium | null {
|
|
return MEDIUM_KEYS.find(medium => MEDIUMS[medium].label === value) ?? null
|
|
}
|
|
|
|
export function lookupValues(values: LookupValue[] | null | undefined): string[] {
|
|
return (values ?? []).map(({ value }) => value?.trim()).filter((value): value is string => !!value)
|
|
}
|
|
|
|
export function toReview(row: ReviewRow): Review {
|
|
return {
|
|
id: row.id,
|
|
date: lookupValues(row.date)[0] ?? '',
|
|
title: lookupValues(row.title).join(' / ') || lookupValues(row.books).join(' / '),
|
|
author: lookupValues(row.author_or_director).join(', ') || null,
|
|
text: (row.review ?? '').replace(/ /g, ' ').trim(),
|
|
medium: toMedium(row.medium?.value)
|
|
}
|
|
}
|
|
|
|
export async function mediumOptionId(event: H3Event, medium: Medium): Promise<number> {
|
|
const options = (await fields(event)).find(field => field.name === 'medium')?.select_options ?? []
|
|
const id = options.find(option => toMedium(option.value) === medium)?.id
|
|
if (!id) {
|
|
throw createError({ statusCode: 500, statusMessage: `The medium field has no "${MEDIUMS[medium].label}" option` })
|
|
}
|
|
return id
|
|
}
|
|
|
|
/** The books table, taken from the reviews table's `books` link field. */
|
|
export async function booksTableId(event: H3Event): Promise<string> {
|
|
const id = (await fields(event)).find(field => field.name === 'books')?.link_row_table_id
|
|
if (!id) {
|
|
throw createError({ statusCode: 500, statusMessage: 'The reviews table has no "books" link field' })
|
|
}
|
|
return String(id)
|
|
}
|