55 lines
1.6 KiB
TypeScript
55 lines
1.6 KiB
TypeScript
import type { ListParams } from '@/types'
|
|
|
|
export const PER_PAGE_OPTIONS = [10, 20, 50] as const
|
|
export const DEFAULT_PER_PAGE = 20
|
|
|
|
export function parseListParams(searchParams: URLSearchParams): ListParams {
|
|
const search = searchParams.get('search') || ''
|
|
const perPage = Math.min(
|
|
Math.max(parseInt(searchParams.get('perPage') || String(DEFAULT_PER_PAGE), 10), 1),
|
|
100,
|
|
)
|
|
const page = Math.max(parseInt(searchParams.get('page') || '1', 10), 1)
|
|
const sort = searchParams.get('sort') || undefined
|
|
const order = (searchParams.get('order') === 'asc' ? 'asc' : 'desc') as 'asc' | 'desc'
|
|
|
|
return { search, perPage, page, sort, order }
|
|
}
|
|
|
|
export function calcPagination(total: number, page: number, perPage: number) {
|
|
const lastPage = Math.max(Math.ceil(total / perPage), 1)
|
|
return {
|
|
total,
|
|
page: Math.min(page, lastPage),
|
|
perPage,
|
|
lastPage,
|
|
offset: (Math.min(page, lastPage) - 1) * perPage,
|
|
}
|
|
}
|
|
|
|
/** Generate CSV content from rows */
|
|
export function generateCsv(
|
|
rows: Record<string, unknown>[],
|
|
columns: { key: string; label: string }[],
|
|
): string {
|
|
const BOM = '\uFEFF'
|
|
const header = columns.map((c) => escapeCsv(c.label)).join(',')
|
|
const body = rows
|
|
.map((row) => columns.map((c) => escapeCsv(String(row[c.key] ?? ''))).join(','))
|
|
.join('\n')
|
|
|
|
return `${BOM + header}\n${body}`
|
|
}
|
|
|
|
function escapeCsv(value: string): string {
|
|
if (value.includes(',') || value.includes('"') || value.includes('\n')) {
|
|
return `"${value.replace(/"/g, '""')}"`
|
|
}
|
|
return value
|
|
}
|
|
|
|
export function formatTimestamp(ts: number): string {
|
|
if (!ts) return 'N/A'
|
|
return new Date(ts * 1000).toLocaleString()
|
|
}
|