import type { DatePreset, ExperimentStatus, SelectOption, PillVariant } from '../types' interface ExperimentCodeSource { id?: number | null experiment_code?: string | null } export function formatExperimentDate(dateStr: string): string { try { return new Date(dateStr).toLocaleDateString(undefined, { year: 'numeric', month: 'short', day: 'numeric', }) } catch { return dateStr } } export function datePresetToISO(preset: DatePreset): string { const now = new Date() const days = preset === 'last_7_days' ? 7 : preset === 'last_30_days' ? 30 : 90 const d = new Date(now.getTime() - days * 86_400_000) return d.toISOString() } export const EXPERIMENT_STATUS_OPTIONS: SelectOption[] = [ { value: '', label: 'All statuses' }, { value: 'planned', label: 'Planned' }, { value: 'ongoing', label: 'Ongoing' }, { value: 'completed', label: 'Completed' }, { value: 'cancelled', label: 'Cancelled' }, ] export const EXPERIMENT_STATUS_VARIANT_MAP: Record = { planned: 'default', ongoing: 'primary', completed: 'success', cancelled: 'error', } export const EXPERIMENT_STATUS_LABELS: Record = { planned: 'Planned', ongoing: 'Ongoing', completed: 'Completed', cancelled: 'Cancelled', } export function formatExperimentStatus(status?: ExperimentStatus | string | null): string { if (!status) return '' if (status in EXPERIMENT_STATUS_LABELS) { return EXPERIMENT_STATUS_LABELS[status as ExperimentStatus] } const label = String(status).replace(/[-_]+/g, ' ').trim() return label ? label.replace(/^\w/, c => c.toUpperCase()) : '' } export function getExperimentStatusVariant(status?: ExperimentStatus | string | null): PillVariant { if (status && status in EXPERIMENT_STATUS_VARIANT_MAP) { return EXPERIMENT_STATUS_VARIANT_MAP[status as ExperimentStatus] } return 'default' } export function resolveExperimentCode(experiment: ExperimentCodeSource): string | undefined { if (experiment.experiment_code) return experiment.experiment_code return undefined } // Mirrors vue-router's LocationQuery, whose array members are `string | null`. // Declaring `string[]` here made the helper reject the very type every caller // passes it, so keep this assignable from LocationQuery without importing // vue-router (the SDK does not depend on it). type RouteQueryValue = string | null | undefined | readonly (string | null | undefined)[] type RouteQueryLike = Record /** * Reads the experiment id a deep link was opened with. * * Returns an id and never a label: a display name belongs to the platform, so * callers must resolve it (see `useExperimentStore().selectById`) rather than * derive one from the query or the id itself. */ export function experimentIdFromRoute(query: RouteQueryLike): number | null { const raw = query.experiment_id ?? query.experimentId const value = Array.isArray(raw) ? raw.find(item => typeof item === 'string') : raw if (typeof value !== 'string' || !value) return null const id = Number(value) return Number.isInteger(id) && id > 0 ? id : null } export const DATE_PRESET_OPTIONS: SelectOption[] = [ { value: '', label: 'Any time' }, { value: 'last_7_days', label: 'Last 7 days' }, { value: 'last_30_days', label: 'Last 30 days' }, { value: 'last_90_days', label: 'Last 90 days' }, ] export const SORT_OPTIONS: SelectOption[] = [ { value: 'created_at:desc', label: 'Newest first' }, { value: 'created_at:asc', label: 'Oldest first' }, { value: 'updated_at:desc', label: 'Recently updated' }, { value: 'name:asc', label: 'Name A\u2013Z' }, { value: 'name:desc', label: 'Name Z\u2013A' }, ]