fix: storybook font rendering and shared fonts module #1
@@ -0,0 +1,72 @@
|
|||||||
|
import type { ListResponse } from './types'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Native fetch wrapper for PocketBase API requests.
|
||||||
|
*/
|
||||||
|
const PB_URL = process.env.NEXT_PUBLIC_PB_URL || 'http://127.0.0.1:8090'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Options for PocketBase collection fetching.
|
||||||
|
*/
|
||||||
|
export type PBFetchOptions = {
|
||||||
|
/**
|
||||||
|
* Sorting criteria (e.g., "-created,order")
|
||||||
|
*/
|
||||||
|
sort?: string
|
||||||
|
/**
|
||||||
|
* Filter query string
|
||||||
|
*/
|
||||||
|
filter?: string
|
||||||
|
/**
|
||||||
|
* Fields to expand (e.g., "stack")
|
||||||
|
*/
|
||||||
|
expand?: string
|
||||||
|
/**
|
||||||
|
* Cache revalidation time in seconds
|
||||||
|
* @default 3600
|
||||||
|
*/
|
||||||
|
revalidate?: number
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch a list of records from a PocketBase collection.
|
||||||
|
*/
|
||||||
|
export async function getCollection<T>(
|
||||||
|
collection: string,
|
||||||
|
options: PBFetchOptions = {}
|
||||||
|
): Promise<ListResponse<T>> {
|
||||||
|
const { sort, filter, expand, revalidate = 3600 } = options
|
||||||
|
|
||||||
|
const params = new URLSearchParams()
|
||||||
|
if (sort) params.set('sort', sort)
|
||||||
|
if (filter) params.set('filter', filter)
|
||||||
|
if (expand) params.set('expand', expand)
|
||||||
|
|
||||||
|
const url = `${PB_URL}/api/collections/${collection}/records?${params.toString()}`
|
||||||
|
|
||||||
|
const res = await fetch(url, {
|
||||||
|
next: { revalidate },
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!res.ok) {
|
||||||
|
throw new Error(`Failed to fetch collection: ${collection}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.json()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch a single record from a PocketBase collection by ID or filter.
|
||||||
|
*/
|
||||||
|
export async function getFirstRecord<T>(
|
||||||
|
collection: string,
|
||||||
|
options: PBFetchOptions = {}
|
||||||
|
): Promise<T | null> {
|
||||||
|
const data = await getCollection<T>(collection, {
|
||||||
|
...options,
|
||||||
|
// PocketBase convention for "first" or "singleton" patterns
|
||||||
|
filter: options.filter,
|
||||||
|
})
|
||||||
|
|
||||||
|
return data.items[0] || null
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
export * from './types'
|
||||||
|
export * from './client'
|
||||||
@@ -0,0 +1,173 @@
|
|||||||
|
/**
|
||||||
|
* Common properties for all PocketBase records.
|
||||||
|
*/
|
||||||
|
export type BaseRecord = {
|
||||||
|
/**
|
||||||
|
* Unique record ID
|
||||||
|
*/
|
||||||
|
id: string
|
||||||
|
/**
|
||||||
|
* ID of the collection this record belongs to
|
||||||
|
*/
|
||||||
|
collectionId: string
|
||||||
|
/**
|
||||||
|
* Name of the collection this record belongs to
|
||||||
|
*/
|
||||||
|
collectionName: string
|
||||||
|
/**
|
||||||
|
* Record creation timestamp (ISO 8601)
|
||||||
|
*/
|
||||||
|
created: string
|
||||||
|
/**
|
||||||
|
* Record last update timestamp (ISO 8601)
|
||||||
|
*/
|
||||||
|
updated: string
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PocketBase collection for site sections and routing.
|
||||||
|
*/
|
||||||
|
export type SectionRecord = BaseRecord & {
|
||||||
|
/**
|
||||||
|
* URL-friendly identifier used for routing
|
||||||
|
*/
|
||||||
|
slug: string
|
||||||
|
/**
|
||||||
|
* Display name of the section
|
||||||
|
*/
|
||||||
|
title: string
|
||||||
|
/**
|
||||||
|
* Visual numbering prefix (e.g., "01")
|
||||||
|
*/
|
||||||
|
number: string
|
||||||
|
/**
|
||||||
|
* Sorting weight for section order
|
||||||
|
*/
|
||||||
|
order: number
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PocketBase collection for simple text blocks (Intro, Bio).
|
||||||
|
*/
|
||||||
|
export type PageContentRecord = BaseRecord & {
|
||||||
|
/**
|
||||||
|
* Slug corresponding to the parent section
|
||||||
|
*/
|
||||||
|
slug: string
|
||||||
|
/**
|
||||||
|
* HTML or Markdown content string
|
||||||
|
*/
|
||||||
|
content: string
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PocketBase collection for technology skills.
|
||||||
|
*/
|
||||||
|
export type SkillRecord = BaseRecord & {
|
||||||
|
/**
|
||||||
|
* Name of the technology or tool
|
||||||
|
*/
|
||||||
|
name: string
|
||||||
|
/**
|
||||||
|
* Grouping category (e.g., 'Frontend', 'Backend')
|
||||||
|
*/
|
||||||
|
category: 'Frontend' | 'Backend' | 'Tools' | 'Design' | string
|
||||||
|
/**
|
||||||
|
* Sorting weight within the category
|
||||||
|
*/
|
||||||
|
order: number
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PocketBase collection for work experience history.
|
||||||
|
*/
|
||||||
|
export type ExperienceRecord = BaseRecord & {
|
||||||
|
/**
|
||||||
|
* Name of the organization
|
||||||
|
*/
|
||||||
|
company: string
|
||||||
|
/**
|
||||||
|
* Professional title held
|
||||||
|
*/
|
||||||
|
role: string
|
||||||
|
/**
|
||||||
|
* Start date of the tenure
|
||||||
|
*/
|
||||||
|
start_date: string
|
||||||
|
/**
|
||||||
|
* End date of the tenure, or null if currently employed
|
||||||
|
*/
|
||||||
|
end_date: string | null
|
||||||
|
/**
|
||||||
|
* Rich text description of responsibilities and achievements
|
||||||
|
*/
|
||||||
|
description: string
|
||||||
|
/**
|
||||||
|
* Sorting weight for chronological display
|
||||||
|
*/
|
||||||
|
order: number
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PocketBase collection for portfolio projects.
|
||||||
|
*/
|
||||||
|
export type ProjectRecord = BaseRecord & {
|
||||||
|
/**
|
||||||
|
* Full title of the project
|
||||||
|
*/
|
||||||
|
title: string
|
||||||
|
/**
|
||||||
|
* Completion or duration year (e.g., "2024")
|
||||||
|
*/
|
||||||
|
year: string
|
||||||
|
/**
|
||||||
|
* Role performed on the project
|
||||||
|
*/
|
||||||
|
role: string
|
||||||
|
/**
|
||||||
|
* Short summary of the project
|
||||||
|
*/
|
||||||
|
description: string
|
||||||
|
/**
|
||||||
|
* List of specific feature or achievement points
|
||||||
|
*/
|
||||||
|
details: string[]
|
||||||
|
/**
|
||||||
|
* List of SkillRecord IDs used in the project
|
||||||
|
*/
|
||||||
|
stack: string[]
|
||||||
|
/**
|
||||||
|
* Primary thumbnail or hero image filename
|
||||||
|
*/
|
||||||
|
image: string
|
||||||
|
/**
|
||||||
|
* Sorting weight for the project list
|
||||||
|
*/
|
||||||
|
order: number
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generic response for a list of PocketBase records.
|
||||||
|
*/
|
||||||
|
export type ListResponse<T> = {
|
||||||
|
/**
|
||||||
|
* Current page index
|
||||||
|
*/
|
||||||
|
page: number
|
||||||
|
/**
|
||||||
|
* Number of items per page
|
||||||
|
*/
|
||||||
|
perPage: number
|
||||||
|
/**
|
||||||
|
* Total number of items across all pages
|
||||||
|
*/
|
||||||
|
totalItems: number
|
||||||
|
/**
|
||||||
|
* Total number of pages available
|
||||||
|
*/
|
||||||
|
totalPages: number
|
||||||
|
/**
|
||||||
|
* Array of records for the current page
|
||||||
|
*/
|
||||||
|
items: T[]
|
||||||
|
}
|
||||||
@@ -1,2 +1,3 @@
|
|||||||
export * from './ui'
|
export * from './ui'
|
||||||
export * from './lib'
|
export * from './lib'
|
||||||
|
export * from './api'
|
||||||
|
|||||||
Reference in New Issue
Block a user