80 lines
2.3 KiB
TypeScript
80 lines
2.3 KiB
TypeScript
import type { H3Event } from 'h3'
|
||
|
||
interface SelectValue {
|
||
value: string
|
||
}
|
||
|
||
interface FileValue {
|
||
url: string
|
||
visible_name: string
|
||
}
|
||
|
||
export interface CvRow {
|
||
id: number
|
||
From: string | null
|
||
To: string | null
|
||
Type: SelectValue | null
|
||
Institution: string | null
|
||
Area: string | null
|
||
area_german?: string | null
|
||
Place: SelectValue | null
|
||
Grade: string | null
|
||
file?: FileValue[] | null
|
||
urls?: string | null
|
||
}
|
||
|
||
export function cvTable(event: H3Event): string {
|
||
const { baserowCvTableId } = useRuntimeConfig(event)
|
||
if (!baserowCvTableId) {
|
||
throw createError({ statusCode: 500, statusMessage: 'NUXT_BASEROW_CV_TABLE_ID is not set' })
|
||
}
|
||
return baserowCvTableId
|
||
}
|
||
|
||
/** Baserow hands out file URLs with its internal protocol (http); serve them from the configured Baserow URL instead */
|
||
function fileUrl(url: string, baserowUrl: string): string {
|
||
const { pathname } = new URL(url)
|
||
return new URL(pathname, baserowUrl).href
|
||
}
|
||
|
||
function toCvLink(line: string): CvLink | null {
|
||
// "https://…", or "Label https://…" for custom link text
|
||
const match = line.match(/^(.*?)[\s:|–-]*(https?:\/\/\S+)$/)
|
||
if (!match) return null
|
||
let url: URL
|
||
try {
|
||
url = new URL(match[2]!)
|
||
} catch {
|
||
return null
|
||
}
|
||
const path = url.pathname.replace(/\/+$/, '')
|
||
const kind = url.hostname === 'github.com' ? 'github' : 'web'
|
||
const fallback = kind === 'github' ? path.slice(1) : url.hostname + path
|
||
return { label: match[1]!.trim() || fallback, url: url.href, kind }
|
||
}
|
||
|
||
/** One URL per line; a blank line starts a new group */
|
||
function toCvLinks(text: string | null | undefined): CvLink[][] {
|
||
return (text ?? '')
|
||
.split(/\n\s*\n/)
|
||
.map(block => block.split('\n').map(line => toCvLink(line.trim())).filter((link): link is CvLink => !!link))
|
||
.filter(group => group.length)
|
||
}
|
||
|
||
export function toCvEntry(row: CvRow, baserowUrl: string): CvEntry | null {
|
||
if (!row.From) return null
|
||
return {
|
||
id: row.id,
|
||
from: row.From,
|
||
to: row.To || null,
|
||
type: (row.Type?.value as CvType | undefined) ?? null,
|
||
institution: (row.Institution ?? '').trim(),
|
||
area: (row.Area ?? '').trim(),
|
||
areaGerman: (row.area_german ?? '').trim(),
|
||
place: row.Place?.value ?? null,
|
||
grade: row.Grade ?? null,
|
||
files: (row.file ?? []).map(file => ({ name: file.visible_name, url: fileUrl(file.url, baserowUrl) })),
|
||
links: toCvLinks(row.urls)
|
||
}
|
||
}
|