feat (v1.0.0): initial refactor and redesign
This commit is contained in:
parent
3058aa1ab4
commit
fe9b50b30e
134 changed files with 17792 additions and 3670 deletions
362
lib/types/ai.ts
Normal file
362
lib/types/ai.ts
Normal file
|
|
@ -0,0 +1,362 @@
|
|||
/**
|
||||
* Type definitions for AI usage analytics and token tracking.
|
||||
*
|
||||
* @remarks
|
||||
* This module contains interfaces for Claude AI usage data, including:
|
||||
* - Token consumption metrics (input, output, cache)
|
||||
* - Cost calculations
|
||||
* - Daily aggregations
|
||||
* - Trend analysis
|
||||
* - Model-specific breakdowns
|
||||
*
|
||||
* @module lib/types/ai
|
||||
* @category Types
|
||||
*/
|
||||
|
||||
/**
|
||||
* Breakdown of AI usage metrics for a specific model.
|
||||
*
|
||||
* @remarks
|
||||
* Contains token counts and cost for a single AI model within a time period.
|
||||
* Used to track usage across different models (e.g., Claude Sonnet, Opus).
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const breakdown: ModelBreakdown = {
|
||||
* modelName: 'claude-sonnet-4-20250514',
|
||||
* inputTokens: 150000,
|
||||
* outputTokens: 75000,
|
||||
* cacheCreationTokens: 5000,
|
||||
* cacheReadTokens: 50000,
|
||||
* cost: 2.45
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface ModelBreakdown {
|
||||
/** Model identifier (e.g., 'claude-sonnet-4-20250514') */
|
||||
modelName: string
|
||||
/** Number of input tokens consumed */
|
||||
inputTokens: number
|
||||
/** Number of output tokens generated */
|
||||
outputTokens: number
|
||||
/** Number of tokens written to cache */
|
||||
cacheCreationTokens: number
|
||||
/** Number of tokens read from cache */
|
||||
cacheReadTokens: number
|
||||
/** Total cost in USD for this model's usage */
|
||||
cost: number
|
||||
}
|
||||
|
||||
/**
|
||||
* Aggregated AI usage data for a single day.
|
||||
*
|
||||
* @remarks
|
||||
* Represents all AI interactions for a 24-hour period, including:
|
||||
* - Total token counts across all models
|
||||
* - Total cost in USD
|
||||
* - Per-model breakdowns
|
||||
* - List of models used
|
||||
*
|
||||
* Date format is ISO 8601 (YYYY-MM-DD).
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const dailyData: DailyData = {
|
||||
* date: '2025-01-15',
|
||||
* inputTokens: 500000,
|
||||
* outputTokens: 250000,
|
||||
* cacheCreationTokens: 10000,
|
||||
* cacheReadTokens: 100000,
|
||||
* totalTokens: 860000,
|
||||
* totalCost: 8.50,
|
||||
* modelsUsed: ['claude-sonnet-4-20250514'],
|
||||
* modelBreakdowns: [...]
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface DailyData {
|
||||
/** Date in ISO 8601 format (YYYY-MM-DD) */
|
||||
date: string
|
||||
/** Total input tokens for the day */
|
||||
inputTokens: number
|
||||
/** Total output tokens for the day */
|
||||
outputTokens: number
|
||||
/** Total cache creation tokens for the day */
|
||||
cacheCreationTokens: number
|
||||
/** Total cache read tokens for the day */
|
||||
cacheReadTokens: number
|
||||
/** Sum of all token types */
|
||||
totalTokens: number
|
||||
/** Total cost in USD for the day */
|
||||
totalCost: number
|
||||
/** List of model identifiers used this day */
|
||||
modelsUsed: string[]
|
||||
/** Per-model usage breakdowns */
|
||||
modelBreakdowns: ModelBreakdown[]
|
||||
}
|
||||
|
||||
/**
|
||||
* Aggregated totals across all time periods.
|
||||
*
|
||||
* @remarks
|
||||
* Represents cumulative usage metrics, typically used for:
|
||||
* - All-time totals
|
||||
* - Custom date range totals
|
||||
* - Filtered subset totals
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const totals: Totals = {
|
||||
* inputTokens: 15000000,
|
||||
* outputTokens: 7500000,
|
||||
* cacheCreationTokens: 100000,
|
||||
* cacheReadTokens: 1000000,
|
||||
* totalCost: 250.00,
|
||||
* totalTokens: 23600000
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface Totals {
|
||||
/** Cumulative input tokens */
|
||||
inputTokens: number
|
||||
/** Cumulative output tokens */
|
||||
outputTokens: number
|
||||
/** Cumulative cache creation tokens */
|
||||
cacheCreationTokens: number
|
||||
/** Cumulative cache read tokens */
|
||||
cacheReadTokens: number
|
||||
/** Cumulative cost in USD */
|
||||
totalCost: number
|
||||
/** Sum of all cumulative tokens */
|
||||
totalTokens: number
|
||||
}
|
||||
|
||||
/**
|
||||
* Complete AI usage dataset with daily breakdowns and totals.
|
||||
*
|
||||
* @remarks
|
||||
* Primary data structure for AI analytics, containing:
|
||||
* - Daily usage history
|
||||
* - Aggregate totals
|
||||
*
|
||||
* Typically loaded from JSON data files or API responses.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const data: CCData = {
|
||||
* daily: [...], // Array of DailyData
|
||||
* totals: {
|
||||
* inputTokens: 15000000,
|
||||
* outputTokens: 7500000,
|
||||
* // ... other totals
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface CCData {
|
||||
/** Array of daily usage data, typically sorted chronologically */
|
||||
daily: DailyData[]
|
||||
/** Aggregated totals across all daily data */
|
||||
totals: Totals
|
||||
}
|
||||
|
||||
/**
|
||||
* Extended AI usage data supporting multiple sources (Claude Code, Codex, etc.).
|
||||
*
|
||||
* @remarks
|
||||
* Used for dashboards that display multiple AI tool usages side-by-side.
|
||||
* Each tool can have its own daily history and totals.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const extended: ExtendedCCData = {
|
||||
* totals: { ... }, // Combined totals
|
||||
* claudeCode: {
|
||||
* daily: [...],
|
||||
* totals: { ... }
|
||||
* },
|
||||
* codex: {
|
||||
* daily: [...],
|
||||
* totals: { ... }
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface ExtendedCCData {
|
||||
/** Combined totals across all sources (optional) */
|
||||
totals?: Totals
|
||||
/** Claude Code usage data */
|
||||
claudeCode?: {
|
||||
daily: DailyData[]
|
||||
totals: Totals
|
||||
}
|
||||
/** Codex usage data */
|
||||
codex?: {
|
||||
daily: DailyData[]
|
||||
totals: Totals
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Time range selector keys for filtering AI usage data.
|
||||
*
|
||||
* @remarks
|
||||
* Used in dropdown menus and filters to select predefined time ranges:
|
||||
* - '7d': Last 7 days
|
||||
* - '1m': Last 1 month
|
||||
* - '3m': Last 3 months
|
||||
* - '6m': Last 6 months
|
||||
* - '1y': Last 1 year
|
||||
* - 'all': All available data
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* function filterByRange(data: DailyData[], range: TimeRangeKey) {
|
||||
* // Implementation
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type TimeRangeKey = '7d' | '1m' | '3m' | '6m' | '1y' | 'all'
|
||||
|
||||
/**
|
||||
* Single day cell data for heatmap visualization.
|
||||
*
|
||||
* @remarks
|
||||
* Contains all data needed to render one cell in a GitHub-style contribution heatmap:
|
||||
* - Date information
|
||||
* - Usage metrics (tokens, cost)
|
||||
* - Display formatting
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const heatmapDay: HeatmapDay = {
|
||||
* date: '2025-01-15',
|
||||
* value: 8.50,
|
||||
* tokens: 860000,
|
||||
* cost: 8.50,
|
||||
* day: 1, // Monday
|
||||
* formattedDate: 'Jan 15, 2025'
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface HeatmapDay {
|
||||
/** Date in ISO 8601 format (YYYY-MM-DD) */
|
||||
date: string
|
||||
/** Primary value for heatmap intensity (typically cost) */
|
||||
value: number
|
||||
/** Total tokens for tooltip display */
|
||||
tokens: number
|
||||
/** Total cost for tooltip display */
|
||||
cost: number
|
||||
/** Day of week (0 = Sunday, 6 = Saturday) */
|
||||
day: number
|
||||
/** Human-readable date string for tooltips */
|
||||
formattedDate: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Daily data extended with trend analysis for chart visualizations.
|
||||
*
|
||||
* @remarks
|
||||
* Extends {@link DailyData} with:
|
||||
* - Linear regression trend lines
|
||||
* - Normalized token values for charting
|
||||
*
|
||||
* Used for displaying trend overlays on usage charts.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const trendData: DailyDataWithTrend = {
|
||||
* ...dailyData,
|
||||
* costTrend: 8.75, // Predicted cost from regression
|
||||
* tokensTrend: 0.9, // Predicted tokens in millions
|
||||
* inputTokensNormalized: 500, // Input tokens / 1000
|
||||
* outputTokensNormalized: 250, // Output tokens / 1000
|
||||
* cacheTokensNormalized: 0.11 // Cache tokens / 1000000
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface DailyDataWithTrend extends DailyData {
|
||||
/** Predicted cost from linear regression (null if not enough data) */
|
||||
costTrend: number | null
|
||||
/** Predicted tokens in millions from linear regression (null if not enough data) */
|
||||
tokensTrend: number | null
|
||||
/** Input tokens divided by 1000 for chart display */
|
||||
inputTokensNormalized: number
|
||||
/** Output tokens divided by 1000 for chart display */
|
||||
outputTokensNormalized: number
|
||||
/** Cache tokens divided by 1000000 for chart display */
|
||||
cacheTokensNormalized: number
|
||||
}
|
||||
|
||||
/**
|
||||
* Model usage statistics for pie/bar charts.
|
||||
*
|
||||
* @remarks
|
||||
* Represents usage distribution across different AI models.
|
||||
* Includes both absolute values and percentages for visualization.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const usage: ModelUsage = {
|
||||
* name: 'Claude Sonnet 4',
|
||||
* value: 125.50,
|
||||
* percentage: 65.2
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface ModelUsage {
|
||||
/** Human-readable model name */
|
||||
name: string
|
||||
/** Total cost or usage value */
|
||||
value: number
|
||||
/** Percentage of total usage (optional) */
|
||||
percentage?: number
|
||||
/** Allow additional properties for chart libraries */
|
||||
[key: string]: string | number | undefined
|
||||
}
|
||||
|
||||
/**
|
||||
* Token type usage statistics for composition charts.
|
||||
*
|
||||
* @remarks
|
||||
* Breaks down usage by token type (input, output, cache creation, cache read).
|
||||
* Used for pie charts showing token distribution.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const tokenUsage: TokenTypeUsage = {
|
||||
* name: 'Input',
|
||||
* value: 15000000,
|
||||
* percentage: 63.5
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface TokenTypeUsage {
|
||||
/** Token type name ('Input', 'Output', 'Cache Creation', 'Cache Read') */
|
||||
name: string
|
||||
/** Total token count */
|
||||
value: number
|
||||
/** Percentage of total tokens (optional) */
|
||||
percentage?: number
|
||||
}
|
||||
83
lib/types/common.ts
Normal file
83
lib/types/common.ts
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
/**
|
||||
* Common utility types used across my website
|
||||
*
|
||||
* @remarks
|
||||
* This module provides fundamental type definitions for date handling,
|
||||
* filtering, and pagination. These types are used throughout
|
||||
* the application to ensure type safety and consistency.
|
||||
*
|
||||
* @module lib/types/common
|
||||
* @category Types
|
||||
* @public
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents a time range with start and end dates.
|
||||
*
|
||||
* @remarks
|
||||
* Used for filtering data by date ranges, such as analytics queries,
|
||||
* domain renewal periods, or session time windows.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* import type { DateRange } from '@/lib/types/common'
|
||||
*
|
||||
* // Last 30 days
|
||||
* const last30Days: DateRange = {
|
||||
* start: new Date(Date.now() - 30 * 24 * 60 * 60 * 1000),
|
||||
* end: new Date()
|
||||
* }
|
||||
*
|
||||
* // Specific date range
|
||||
* const Q1: DateRange = {
|
||||
* start: new Date('2025-01-01'),
|
||||
* end: new Date('2025-03-31')
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @category Types
|
||||
* @public
|
||||
*/
|
||||
export interface DateRange {
|
||||
/** Start date of the range (inclusive) */
|
||||
start: Date
|
||||
/** End date of the range (inclusive) */
|
||||
end: Date
|
||||
}
|
||||
|
||||
/**
|
||||
* Date object with pre-computed formatted strings.
|
||||
*
|
||||
* @remarks
|
||||
* This type is useful when you need to display dates in multiple formats
|
||||
* and want to avoid repeated formatting operations. Commonly used in
|
||||
* components that display dates in both human-readable and machine-readable formats.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* import type { FormattedDate } from '@/lib/types/common'
|
||||
* import { Formatter } from '@/lib/utils'
|
||||
*
|
||||
* const registrationDate: FormattedDate = {
|
||||
* date: new Date('2023-05-15'),
|
||||
* formatted: 'May 15, 2023',
|
||||
* iso: '2023-05-15T00:00:00.000Z'
|
||||
* }
|
||||
*
|
||||
* // Usage in components
|
||||
* <time dateTime={registrationDate.iso}>
|
||||
* {registrationDate.formatted}
|
||||
* </time>
|
||||
* ```
|
||||
*
|
||||
* @category Types
|
||||
* @public
|
||||
*/
|
||||
export interface FormattedDate {
|
||||
/** Original Date object */
|
||||
date: Date
|
||||
/** Human-readable formatted string (e.g., "May 15, 2023") */
|
||||
formatted: string
|
||||
/** ISO 8601 formatted string for machine readability */
|
||||
iso: string
|
||||
}
|
||||
517
lib/types/device.ts
Normal file
517
lib/types/device.ts
Normal file
|
|
@ -0,0 +1,517 @@
|
|||
/**
|
||||
* Device type definitions for portfolio showcase.
|
||||
*
|
||||
* Provides comprehensive type safety for device specifications, statistics,
|
||||
* sections, and UI components. Supports both mobile devices and DAPs (Digital Audio Players).
|
||||
*
|
||||
* @module lib/types/device
|
||||
* @category Types
|
||||
*/
|
||||
|
||||
import React from 'react'
|
||||
|
||||
/**
|
||||
* Icon component type for device-related icons.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type DeviceIcon = React.ComponentType<{
|
||||
/** Optional className for styling */
|
||||
className?: string
|
||||
/** Optional size override */
|
||||
size?: number
|
||||
}>
|
||||
|
||||
/**
|
||||
* Device type classification.
|
||||
*
|
||||
* @remarks
|
||||
* - `mobile`: Smartphones and mobile devices
|
||||
* - `dap`: Digital Audio Players (dedicated music players)
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const type: DeviceType = 'mobile'
|
||||
* ```
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type DeviceType = 'mobile' | 'dap'
|
||||
|
||||
/**
|
||||
* Star rating display state.
|
||||
*
|
||||
* @remarks
|
||||
* Used for rendering star ratings with half-star support.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type StarState = 'full' | 'half' | 'empty'
|
||||
|
||||
/**
|
||||
* Type-safe external URL starting with http or https.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const url: ExternalHref = 'https://example.com'
|
||||
* ```
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type ExternalHref = `http${string}`
|
||||
|
||||
/**
|
||||
* Badge display configuration for device highlights.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const badge: DeviceBadge = {
|
||||
* label: 'Flagship',
|
||||
* tone: 'highlight'
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface DeviceBadge {
|
||||
/** Badge text */
|
||||
label: string
|
||||
|
||||
/** Visual tone (default: neutral, highlight: accent, muted: subtle) */
|
||||
tone?: 'default' | 'highlight' | 'muted'
|
||||
}
|
||||
|
||||
/**
|
||||
* Individual stat item within a stat group.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const stat: DeviceStatItem = {
|
||||
* label: 'Display',
|
||||
* value: '6.1" OLED',
|
||||
* href: 'https://example.com/display-specs'
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface DeviceStatItem {
|
||||
/** Optional label for the stat */
|
||||
label?: string
|
||||
|
||||
/** Stat value to display */
|
||||
value: string
|
||||
|
||||
/** Optional external link for more information */
|
||||
href?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Group of related device statistics.
|
||||
*
|
||||
* @example
|
||||
* ```tsx
|
||||
* import { CpuIcon } from 'lucide-react'
|
||||
*
|
||||
* const group: DeviceStatGroup = {
|
||||
* title: 'Performance',
|
||||
* icon: CpuIcon,
|
||||
* accent: 'primary',
|
||||
* items: [
|
||||
* { label: 'Processor', value: 'Snapdragon 8 Gen 2' },
|
||||
* { label: 'RAM', value: '8GB' },
|
||||
* { label: 'Storage', value: '256GB' }
|
||||
* ]
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface DeviceStatGroup {
|
||||
/** Group title */
|
||||
title: string
|
||||
|
||||
/** Optional icon for visual identification */
|
||||
icon?: DeviceIcon
|
||||
|
||||
/** List of stat items in this group */
|
||||
items: DeviceStatItem[]
|
||||
|
||||
/** Visual accent style */
|
||||
accent?: 'primary' | 'surface'
|
||||
}
|
||||
|
||||
/**
|
||||
* Row item within a device section showing key-value pairs.
|
||||
*
|
||||
* @example
|
||||
* ```tsx
|
||||
* import { BatteryIcon } from 'lucide-react'
|
||||
*
|
||||
* const row: DeviceSectionRow = {
|
||||
* label: 'Battery',
|
||||
* value: '5000 mAh',
|
||||
* icon: BatteryIcon,
|
||||
* note: 'Supports 65W fast charging'
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface DeviceSectionRow {
|
||||
/** Row label */
|
||||
label: string
|
||||
|
||||
/** Row value */
|
||||
value: string
|
||||
|
||||
/** Optional icon */
|
||||
icon?: DeviceIcon
|
||||
|
||||
/** Optional external link */
|
||||
href?: string
|
||||
|
||||
/** Optional additional note */
|
||||
note?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* List item within a device section.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const item: DeviceSectionListItem = {
|
||||
* label: 'USB-C 3.1',
|
||||
* description: 'Fast data transfer and charging',
|
||||
* href: 'https://example.com/usb-specs'
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface DeviceSectionListItem {
|
||||
/** Item label */
|
||||
label: string
|
||||
|
||||
/** Optional description */
|
||||
description?: string
|
||||
|
||||
/** Optional external link */
|
||||
href?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Rating configuration for device sections.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const rating: DeviceSectionRating = {
|
||||
* value: 4.5,
|
||||
* scale: 5,
|
||||
* label: 'Overall Rating'
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface DeviceSectionRating {
|
||||
/** Rating value (supports half-stars) */
|
||||
value: number
|
||||
|
||||
/** Maximum rating scale (default: 5) */
|
||||
scale?: number
|
||||
|
||||
/** Optional rating label */
|
||||
label?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Device section containing grouped information.
|
||||
*
|
||||
* @remarks
|
||||
* Sections can contain one of: rows (key-value pairs), listItems (bullet lists),
|
||||
* paragraphs (text content), or rating (star rating). Each section has an icon
|
||||
* for visual identification.
|
||||
*
|
||||
* @example
|
||||
* ```tsx
|
||||
* import { CameraIcon } from 'lucide-react'
|
||||
*
|
||||
* const section: DeviceSection = {
|
||||
* id: 'camera',
|
||||
* title: 'Camera',
|
||||
* icon: CameraIcon,
|
||||
* rows: [
|
||||
* { label: 'Main', value: '50MP f/1.8' },
|
||||
* { label: 'Ultra-wide', value: '12MP f/2.2' },
|
||||
* { label: 'Telephoto', value: '10MP f/2.4' }
|
||||
* ],
|
||||
* rating: { value: 4.5, scale: 5 }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface DeviceSection {
|
||||
/** Unique section identifier */
|
||||
id: string
|
||||
|
||||
/** Section title */
|
||||
title: string
|
||||
|
||||
/** Section icon */
|
||||
icon: DeviceIcon
|
||||
|
||||
/** Optional key-value rows */
|
||||
rows?: DeviceSectionRow[]
|
||||
|
||||
/** Optional list items */
|
||||
listItems?: DeviceSectionListItem[]
|
||||
|
||||
/** Optional text paragraphs */
|
||||
paragraphs?: string[]
|
||||
|
||||
/** Optional rating */
|
||||
rating?: DeviceSectionRating
|
||||
}
|
||||
|
||||
/**
|
||||
* Complete device specification.
|
||||
*
|
||||
* Contains all data needed to render a device page including metadata, statistics,
|
||||
* sections, and related devices.
|
||||
*
|
||||
* @example
|
||||
* ```tsx
|
||||
* import { CpuIcon, BatteryIcon } from 'lucide-react'
|
||||
*
|
||||
* const device: DeviceSpec = {
|
||||
* slug: 'pixel-8-pro',
|
||||
* name: 'Google Pixel 8 Pro',
|
||||
* codename: 'husky',
|
||||
* type: 'mobile',
|
||||
* manufacturer: 'Google',
|
||||
* shortName: 'Pixel 8 Pro',
|
||||
* status: 'Current',
|
||||
* releaseYear: 2023,
|
||||
* heroImage: {
|
||||
* src: '/img/devices/pixel-8-pro.png',
|
||||
* alt: 'Google Pixel 8 Pro',
|
||||
* width: 800,
|
||||
* height: 600
|
||||
* },
|
||||
* tagline: 'AI-powered flagship smartphone',
|
||||
* summary: [
|
||||
* 'Advanced Tensor G3 processor',
|
||||
* 'Exceptional camera system',
|
||||
* 'Premium build quality'
|
||||
* ],
|
||||
* badges: [
|
||||
* { label: 'Flagship', tone: 'highlight' },
|
||||
* { label: 'Current', tone: 'default' }
|
||||
* ],
|
||||
* stats: [
|
||||
* {
|
||||
* title: 'Performance',
|
||||
* icon: CpuIcon,
|
||||
* items: [
|
||||
* { label: 'Processor', value: 'Google Tensor G3' },
|
||||
* { label: 'RAM', value: '12GB' }
|
||||
* ]
|
||||
* }
|
||||
* ],
|
||||
* sections: [
|
||||
* {
|
||||
* id: 'battery',
|
||||
* title: 'Battery',
|
||||
* icon: BatteryIcon,
|
||||
* rows: [{ label: 'Capacity', value: '5050 mAh' }]
|
||||
* }
|
||||
* ],
|
||||
* related: ['pixel-7-pro', 'pixel-8'],
|
||||
* updatedAt: '2024-01-15'
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface DeviceSpec {
|
||||
/** URL-friendly slug */
|
||||
slug: string
|
||||
|
||||
/** Full device name */
|
||||
name: string
|
||||
|
||||
/** Optional device codename */
|
||||
codename?: string
|
||||
|
||||
/** Device type (mobile or dap) */
|
||||
type: DeviceType
|
||||
|
||||
/** Manufacturer name */
|
||||
manufacturer?: string
|
||||
|
||||
/** Short display name */
|
||||
shortName?: string
|
||||
|
||||
/** Current status (e.g., 'Current', 'Retired') */
|
||||
status?: string
|
||||
|
||||
/** Year of release */
|
||||
releaseYear?: number
|
||||
|
||||
/** Hero image configuration */
|
||||
heroImage: {
|
||||
/** Image source path */
|
||||
src: string
|
||||
/** Alt text for accessibility */
|
||||
alt: string
|
||||
/** Optional width */
|
||||
width?: number
|
||||
/** Optional height */
|
||||
height?: number
|
||||
}
|
||||
|
||||
/** Marketing tagline */
|
||||
tagline?: string
|
||||
|
||||
/** Summary bullet points */
|
||||
summary?: string[]
|
||||
|
||||
/** Feature badges */
|
||||
badges?: DeviceBadge[]
|
||||
|
||||
/** Stat groups */
|
||||
stats: DeviceStatGroup[]
|
||||
|
||||
/** Content sections */
|
||||
sections: DeviceSection[]
|
||||
|
||||
/** Related device slugs */
|
||||
related?: string[]
|
||||
|
||||
/** Last update date (ISO format) */
|
||||
updatedAt?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Collection of devices indexed by slug.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type DeviceCollection = Record<string, DeviceSpec>
|
||||
|
||||
/**
|
||||
* Enriched device with computed metrics.
|
||||
*
|
||||
* Extends {@link DeviceSpec} with age calculations and display labels.
|
||||
*
|
||||
* @remarks
|
||||
* This interface is generated by `DeviceService.enrichDevice()` method.
|
||||
* All devices returned by DeviceService methods include these computed properties.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* import { DeviceService } from '@/lib/services'
|
||||
*
|
||||
* const enriched: DeviceWithMetrics = DeviceService.enrichDevice(device)
|
||||
* console.log(enriched.ageInYears) // 1
|
||||
* console.log(enriched.isCurrentYear) // false
|
||||
* console.log(enriched.categoryLabel) // 'Mobile Devices'
|
||||
* ```
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface DeviceWithMetrics extends DeviceSpec {
|
||||
/** Device age in full years */
|
||||
ageInYears: number
|
||||
|
||||
/** True if released in current year */
|
||||
isCurrentYear: boolean
|
||||
|
||||
/** Display label for device category */
|
||||
categoryLabel: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Props for DevicePageShell component.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface DevicePageShellProps {
|
||||
/** Device data to render */
|
||||
device: DeviceSpec
|
||||
}
|
||||
|
||||
/**
|
||||
* Props for DeviceHero component.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface DeviceHeroProps {
|
||||
/** Device data for hero section */
|
||||
device: DeviceSpec
|
||||
}
|
||||
|
||||
/**
|
||||
* Props for StatsGrid component.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface StatsGridProps {
|
||||
/** Stat groups to display */
|
||||
stats: DeviceStatGroup[]
|
||||
}
|
||||
|
||||
/**
|
||||
* Props for StatItem component.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface StatItemProps {
|
||||
/** Stat item to display */
|
||||
item: DeviceStatItem
|
||||
|
||||
/** Optional group icon for fallback */
|
||||
groupIcon?: DeviceStatGroup['icon']
|
||||
}
|
||||
|
||||
/**
|
||||
* Props for SectionsGrid component.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface SectionsGridProps {
|
||||
/** Sections to display in grid */
|
||||
sections: DeviceSection[]
|
||||
}
|
||||
|
||||
/**
|
||||
* Props for SectionCard component.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface SectionCardProps {
|
||||
/** Section data to render */
|
||||
section: DeviceSection
|
||||
}
|
||||
|
||||
/**
|
||||
* Props for SectionRow component.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface SectionRowProps {
|
||||
/** Row data to render */
|
||||
row: NonNullable<DeviceSection['rows']>[number]
|
||||
}
|
||||
|
||||
/**
|
||||
* Props for Rating component.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface RatingProps {
|
||||
/** Rating data to render */
|
||||
rating: NonNullable<DeviceSection['rating']>
|
||||
}
|
||||
380
lib/types/domain.ts
Normal file
380
lib/types/domain.ts
Normal file
|
|
@ -0,0 +1,380 @@
|
|||
/**
|
||||
* Domain type definitions for portfolio management.
|
||||
*
|
||||
* Provides comprehensive type safety for domain data, metrics, and UI components.
|
||||
* All domain-related interfaces include renewal tracking, ownership metrics, and
|
||||
* categorization for portfolio organization.
|
||||
*
|
||||
* @module lib/types/domain
|
||||
* @category Types
|
||||
*/
|
||||
|
||||
import type { ComponentType, Dispatch, SetStateAction } from 'react'
|
||||
import type { IconType } from 'react-icons'
|
||||
import type { LucideIcon } from 'lucide-react'
|
||||
|
||||
/**
|
||||
* Domain status indicating current usage state.
|
||||
*
|
||||
* @remarks
|
||||
* - `active`: Domain is actively being used for a website or service
|
||||
* - `parked`: Domain is registered but not currently in use
|
||||
* - `reserved`: Domain is reserved for future use
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const status: DomainStatus = 'active'
|
||||
* ```
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type DomainStatus = 'active' | 'parked' | 'reserved'
|
||||
|
||||
/**
|
||||
* Domain category for portfolio organization.
|
||||
*
|
||||
* @remarks
|
||||
* Categories help organize domains by purpose and importance:
|
||||
* - `personal`: Personal branding or portfolio domains
|
||||
* - `service`: Production services and applications
|
||||
* - `project`: Project-specific or experimental domains
|
||||
* - `fun`: Hobby or entertainment domains
|
||||
* - `legacy`: Older domains maintained for historical reasons
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const category: DomainCategory = 'service'
|
||||
* ```
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type DomainCategory = 'personal' | 'service' | 'project' | 'fun' | 'legacy'
|
||||
|
||||
/**
|
||||
* Supported domain registrar identifiers.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const registrar: DomainRegistrarId = 'Namecheap'
|
||||
* ```
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type DomainRegistrarId = 'Spaceship' | 'Namecheap' | 'Name.com' | 'Dynadot'
|
||||
|
||||
/**
|
||||
* Sort options for domain lists.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const sortBy: DomainSortOption = 'expiration'
|
||||
* ```
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type DomainSortOption = 'name' | 'expiration' | 'ownership' | 'registrar'
|
||||
|
||||
/**
|
||||
* Timeline event types for domain history.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const eventType: DomainTimelineEventType = 'renewal'
|
||||
* ```
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type DomainTimelineEventType = 'registration' | 'renewal'
|
||||
|
||||
/**
|
||||
* Domain renewal record tracking renewal history.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const renewal: Renewal = {
|
||||
* date: '2024-01-15',
|
||||
* years: 2
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface Renewal {
|
||||
/** ISO date string of renewal (YYYY-MM-DD format) */
|
||||
date: string
|
||||
|
||||
/** Number of years renewed */
|
||||
years: number
|
||||
}
|
||||
|
||||
/**
|
||||
* Registrar configuration for UI display.
|
||||
*
|
||||
* @example
|
||||
* ```tsx
|
||||
* import { SiNamecheap } from 'react-icons/si'
|
||||
*
|
||||
* const config: RegistrarConfig = {
|
||||
* name: 'Namecheap',
|
||||
* icon: SiNamecheap,
|
||||
* color: '#FF6C2C'
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface RegistrarConfig {
|
||||
/** Display name of the registrar */
|
||||
name: string
|
||||
|
||||
/** Icon component (from react-icons or custom) */
|
||||
icon: IconType | ComponentType<{className?: string}>
|
||||
|
||||
/** Brand color for visual identification */
|
||||
color: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Core domain data structure.
|
||||
*
|
||||
* Base interface containing all raw domain information without computed metrics.
|
||||
* Use {@link DomainWithMetrics} for enriched data with ownership calculations.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const domain: Domain = {
|
||||
* domain: 'example.com',
|
||||
* usage: 'Personal portfolio website',
|
||||
* registrar: 'Namecheap',
|
||||
* autoRenew: true,
|
||||
* status: 'active',
|
||||
* category: 'personal',
|
||||
* tags: ['portfolio', 'web'],
|
||||
* renewals: [
|
||||
* { date: '2022-01-15', years: 1 },
|
||||
* { date: '2023-01-15', years: 2 }
|
||||
* ]
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface Domain {
|
||||
/** Domain name (e.g., 'example.com') */
|
||||
domain: string
|
||||
|
||||
/** Description of domain usage/purpose */
|
||||
usage: string
|
||||
|
||||
/** Registrar where domain is registered */
|
||||
registrar: DomainRegistrarId
|
||||
|
||||
/** Whether auto-renewal is enabled */
|
||||
autoRenew: boolean
|
||||
|
||||
/** Current status of the domain */
|
||||
status: DomainStatus
|
||||
|
||||
/** Portfolio category for organization */
|
||||
category: DomainCategory
|
||||
|
||||
/** Tags for filtering and search */
|
||||
tags: string[]
|
||||
|
||||
/** Renewal history (chronological order) */
|
||||
renewals: Renewal[]
|
||||
}
|
||||
|
||||
/**
|
||||
* Enriched domain data with computed ownership and expiration metrics.
|
||||
*
|
||||
* Extends {@link Domain} with calculated fields for ownership duration, expiration
|
||||
* tracking, and renewal progress.
|
||||
*
|
||||
* @remarks
|
||||
* All date calculations use UTC to ensure consistent timezone handling. The renewal
|
||||
* progress percentage helps visualize time until next renewal is needed.
|
||||
*
|
||||
* This interface is generated by `DomainService.enrichDomain()` method.
|
||||
* All domains returned by DomainService methods include these computed properties.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* import { DomainService } from '@/lib/services'
|
||||
*
|
||||
* const enriched: DomainWithMetrics = DomainService.enrichDomain(domain)
|
||||
* console.log(enriched.ownershipYears) // 2
|
||||
* console.log(enriched.daysUntilExpiration) // 347
|
||||
* console.log(enriched.isExpiringSoon) // false
|
||||
* console.log(enriched.renewalProgressPercent) // 15.2
|
||||
* console.log(enriched.tld) // 'com'
|
||||
* ```
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface DomainWithMetrics extends Domain {
|
||||
/** Calculated expiration date based on last renewal */
|
||||
expirationDate: Date
|
||||
|
||||
/** First renewal date (registration date) */
|
||||
registrationDate: Date
|
||||
|
||||
/** Total days of ownership */
|
||||
ownershipDays: number
|
||||
|
||||
/** Full years of ownership (floored) */
|
||||
ownershipYears: number
|
||||
|
||||
/** Remaining months after full years */
|
||||
ownershipMonths: number
|
||||
|
||||
/** Days until domain expires */
|
||||
daysUntilExpiration: number
|
||||
|
||||
/** Percentage of current renewal period completed (0-100) */
|
||||
renewalProgressPercent: number
|
||||
|
||||
/** True if expiring within 90 days */
|
||||
isExpiringSoon: boolean
|
||||
|
||||
/** Date of next scheduled renewal */
|
||||
nextRenewalDate: Date
|
||||
|
||||
/** Top-level domain extension (e.g., 'com', 'net') */
|
||||
tld: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Visual styling configuration for domain status/category badges.
|
||||
*
|
||||
* @example
|
||||
* ```tsx
|
||||
* import { Circle } from 'lucide-react'
|
||||
*
|
||||
* const option: DomainVisualOption = {
|
||||
* label: 'Active',
|
||||
* icon: Circle,
|
||||
* color: 'text-green-400',
|
||||
* bg: 'bg-green-500/20',
|
||||
* border: 'border-green-500/30'
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface DomainVisualOption {
|
||||
/** Display label text */
|
||||
label: string
|
||||
|
||||
/** Lucide icon component */
|
||||
icon: LucideIcon
|
||||
|
||||
/** Tailwind text color class */
|
||||
color: string
|
||||
|
||||
/** Tailwind background color class */
|
||||
bg: string
|
||||
|
||||
/** Tailwind border color class */
|
||||
border: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Complete visual configuration mapping for all domain statuses and categories.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type DomainVisualConfig = {
|
||||
/** Visual options for each status type */
|
||||
status: Record<DomainStatus, DomainVisualOption>
|
||||
|
||||
/** Visual options for each category type */
|
||||
category: Record<DomainCategory, DomainVisualOption>
|
||||
}
|
||||
|
||||
/**
|
||||
* Mapping of registrar IDs to their configuration.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type DomainRegistrarMap = Record<DomainRegistrarId, RegistrarConfig>
|
||||
|
||||
/**
|
||||
* Domain timeline event for renewal/registration history display.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const event: DomainTimelineEvent = {
|
||||
* date: new Date('2024-01-15'),
|
||||
* type: 'renewal',
|
||||
* years: 2
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface DomainTimelineEvent {
|
||||
/** Event date */
|
||||
date: Date
|
||||
|
||||
/** Event type (registration or renewal) */
|
||||
type: DomainTimelineEventType
|
||||
|
||||
/** Number of years for the renewal */
|
||||
years: number
|
||||
}
|
||||
|
||||
/**
|
||||
* Props for DomainCard component.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface DomainCardProps {
|
||||
/** Domain data to display */
|
||||
domain: Domain
|
||||
}
|
||||
|
||||
/**
|
||||
* Props for DomainDetails component.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface DomainDetailsProps {
|
||||
/** Domain data to display */
|
||||
domain: Domain
|
||||
}
|
||||
|
||||
/**
|
||||
* Props for DomainTimeline component.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface DomainTimelineProps {
|
||||
/** Domain data to display timeline for */
|
||||
domain: Domain
|
||||
}
|
||||
|
||||
/**
|
||||
* Props for DomainFilters component.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface DomainFiltersProps {
|
||||
/** Search query change handler */
|
||||
onSearchChange: Dispatch<SetStateAction<string>>
|
||||
|
||||
/** Category filter change handler */
|
||||
onCategoryChange: Dispatch<SetStateAction<DomainCategory[]>>
|
||||
|
||||
/** Status filter change handler */
|
||||
onStatusChange: Dispatch<SetStateAction<DomainStatus[]>>
|
||||
|
||||
/** Registrar filter change handler */
|
||||
onRegistrarChange: Dispatch<SetStateAction<DomainRegistrarId[]>>
|
||||
|
||||
/** Sort option change handler */
|
||||
onSortChange: Dispatch<SetStateAction<DomainSortOption>>
|
||||
|
||||
/** Available registrars for filter options */
|
||||
registrars: DomainRegistrarId[]
|
||||
}
|
||||
6
lib/types/index.ts
Normal file
6
lib/types/index.ts
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
export * from './domain'
|
||||
export * from './device'
|
||||
export * from './ai'
|
||||
export * from './common'
|
||||
export * from './navigation'
|
||||
export * from './service'
|
||||
71
lib/types/navigation.ts
Normal file
71
lib/types/navigation.ts
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
import type { ComponentType, ReactNode } from 'react'
|
||||
|
||||
import type { GitHubRepoSummary } from '@/lib/github'
|
||||
|
||||
export type NavigationIcon = ComponentType<{ className?: string; size?: number } & Record<string, unknown>>
|
||||
|
||||
export type NavigationLink = {
|
||||
label: string
|
||||
href: string
|
||||
icon: NavigationIcon
|
||||
external?: boolean
|
||||
}
|
||||
|
||||
export type NavigationDropdownLinkItem = NavigationLink & {
|
||||
type: 'link'
|
||||
}
|
||||
|
||||
export type NavigationDropdownGroup = {
|
||||
title: string
|
||||
links: NavigationDropdownLinkItem[]
|
||||
}
|
||||
|
||||
export type NavigationDropdownNestedItem = {
|
||||
type: 'nested'
|
||||
label: string
|
||||
icon: NavigationIcon
|
||||
groups: NavigationDropdownGroup[]
|
||||
}
|
||||
|
||||
export type NavigationDropdownItem =
|
||||
| NavigationDropdownLinkItem
|
||||
| NavigationDropdownNestedItem
|
||||
|
||||
export type NavigationDropdownConfig = {
|
||||
items: NavigationDropdownItem[]
|
||||
}
|
||||
|
||||
export type NavigationMenuLinkItem = NavigationLink & {
|
||||
type: 'link'
|
||||
id: string
|
||||
}
|
||||
|
||||
export type NavigationMenuDropdownItem = {
|
||||
type: 'dropdown'
|
||||
id: string
|
||||
label: string
|
||||
href: string
|
||||
icon: NavigationIcon
|
||||
dropdown: NavigationDropdownConfig
|
||||
}
|
||||
|
||||
export type NavigationMenuItem =
|
||||
| NavigationMenuLinkItem
|
||||
| NavigationMenuDropdownItem
|
||||
|
||||
export type FooterMenuRenderContext = {
|
||||
githubUsername: string
|
||||
githubRepos: GitHubRepoSummary[]
|
||||
}
|
||||
|
||||
export type FooterMenuSection =
|
||||
| {
|
||||
type: 'links'
|
||||
title: string
|
||||
links: NavigationLink[]
|
||||
}
|
||||
| {
|
||||
type: 'custom'
|
||||
title: string
|
||||
render: (context: FooterMenuRenderContext) => ReactNode
|
||||
}
|
||||
376
lib/types/service.ts
Normal file
376
lib/types/service.ts
Normal file
|
|
@ -0,0 +1,376 @@
|
|||
/**
|
||||
* Shared type definitions for service layer operations.
|
||||
*
|
||||
* @remarks
|
||||
* This module contains reusable types for common service patterns:
|
||||
* - Filtering and sorting configurations
|
||||
* - Statistics and aggregation results
|
||||
* - Query options and pagination
|
||||
*
|
||||
* @module lib/types/service
|
||||
* @category Types
|
||||
*/
|
||||
|
||||
/**
|
||||
* Sort order direction for list operations.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const order: SortOrder = 'asc' // Ascending order
|
||||
* const descOrder: SortOrder = 'desc' // Descending order
|
||||
* ```
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type SortOrder = 'asc' | 'desc'
|
||||
|
||||
/**
|
||||
* Configuration for sorting operations on typed entities.
|
||||
*
|
||||
* @template T - The entity type being sorted
|
||||
*
|
||||
* @remarks
|
||||
* Provides type-safe sorting configuration with:
|
||||
* - Compile-time verification of sort key
|
||||
* - Order direction (ascending/descending)
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* interface User {
|
||||
* name: string
|
||||
* age: number
|
||||
* createdAt: Date
|
||||
* }
|
||||
*
|
||||
* const config: SortConfig<User> = {
|
||||
* sortBy: 'age',
|
||||
* order: 'desc'
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface SortConfig<T> {
|
||||
/** Property key to sort by (type-safe) */
|
||||
sortBy: keyof T
|
||||
/** Sort direction */
|
||||
order: SortOrder
|
||||
}
|
||||
|
||||
/**
|
||||
* Generic filter configuration for entity queries.
|
||||
*
|
||||
* @template T - The entity type being filtered
|
||||
*
|
||||
* @remarks
|
||||
* Provides a flexible filtering system where:
|
||||
* - Keys are entity properties
|
||||
* - Values can be exact matches or undefined (no filter)
|
||||
*
|
||||
* This enables type-safe, partial filtering of entities.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* interface Product {
|
||||
* category: string
|
||||
* inStock: boolean
|
||||
* price: number
|
||||
* }
|
||||
*
|
||||
* const filters: FilterConfig<Product> = {
|
||||
* category: 'electronics',
|
||||
* inStock: true
|
||||
* // price is omitted (not filtered)
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type FilterConfig<T> = Partial<T>
|
||||
|
||||
/**
|
||||
* Statistics result containing aggregate metrics.
|
||||
*
|
||||
* @remarks
|
||||
* Common return type for service methods that compute statistics.
|
||||
* Includes:
|
||||
* - Total count
|
||||
* - Category/group breakdowns
|
||||
* - Additional metrics as key-value pairs
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const stats: StatsResult = {
|
||||
* total: 150,
|
||||
* byCategory: {
|
||||
* active: 120,
|
||||
* inactive: 30
|
||||
* },
|
||||
* averageAge: 2.5,
|
||||
* newestItem: {...}
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface StatsResult {
|
||||
/** Total count of items */
|
||||
total: number
|
||||
/** Breakdown by categories */
|
||||
byCategory?: Record<string, number>
|
||||
/** Additional computed metrics */
|
||||
[key: string]: number | Record<string, number> | unknown | undefined
|
||||
}
|
||||
|
||||
/**
|
||||
* Date range configuration for time-based queries.
|
||||
*
|
||||
* @remarks
|
||||
* Supports both absolute dates and relative ranges.
|
||||
* Used for filtering time-series data.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* // Absolute range
|
||||
* const range: DateRangeConfig = {
|
||||
* start: new Date('2025-01-01'),
|
||||
* end: new Date('2025-01-31')
|
||||
* }
|
||||
*
|
||||
* // Relative range
|
||||
* const lastMonth: DateRangeConfig = {
|
||||
* relativeDays: 30
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface DateRangeConfig {
|
||||
/** Start date (inclusive) */
|
||||
start?: Date
|
||||
/** End date (inclusive) */
|
||||
end?: Date
|
||||
/** Relative days from now (alternative to start/end) */
|
||||
relativeDays?: number
|
||||
/** Relative months from now (alternative to start/end) */
|
||||
relativeMonths?: number
|
||||
}
|
||||
|
||||
/**
|
||||
* Pagination configuration for list operations.
|
||||
*
|
||||
* @remarks
|
||||
* Standard pagination pattern with:
|
||||
* - Page number (1-indexed)
|
||||
* - Items per page
|
||||
* - Optional offset-based pagination
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* // Page-based pagination
|
||||
* const config: PaginationConfig = {
|
||||
* page: 2,
|
||||
* pageSize: 25
|
||||
* }
|
||||
*
|
||||
* // Offset-based pagination
|
||||
* const offsetConfig: PaginationConfig = {
|
||||
* page: 1,
|
||||
* pageSize: 25,
|
||||
* offset: 50
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface PaginationConfig {
|
||||
/** Current page number (1-indexed) */
|
||||
page: number
|
||||
/** Number of items per page */
|
||||
pageSize: number
|
||||
/** Optional offset for cursor-based pagination */
|
||||
offset?: number
|
||||
}
|
||||
|
||||
/**
|
||||
* Paginated result set with metadata.
|
||||
*
|
||||
* @template T - Type of items in the result
|
||||
*
|
||||
* @remarks
|
||||
* Standard response format for paginated queries, including:
|
||||
* - Data items
|
||||
* - Total count
|
||||
* - Current page info
|
||||
* - Navigation metadata
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const result: PaginatedResult<User> = {
|
||||
* items: [...],
|
||||
* total: 150,
|
||||
* page: 2,
|
||||
* pageSize: 25,
|
||||
* totalPages: 6,
|
||||
* hasMore: true
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface PaginatedResult<T> {
|
||||
/** Items for current page */
|
||||
items: T[]
|
||||
/** Total count across all pages */
|
||||
total: number
|
||||
/** Current page number */
|
||||
page: number
|
||||
/** Items per page */
|
||||
pageSize: number
|
||||
/** Total number of pages */
|
||||
totalPages: number
|
||||
/** Whether more pages exist */
|
||||
hasMore: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* Query options combining common patterns.
|
||||
*
|
||||
* @template T - Entity type being queried
|
||||
*
|
||||
* @remarks
|
||||
* Comprehensive query configuration supporting:
|
||||
* - Filtering
|
||||
* - Sorting
|
||||
* - Pagination
|
||||
* - Date ranges
|
||||
*
|
||||
* Designed for flexible, composable queries.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* interface Product {
|
||||
* name: string
|
||||
* price: number
|
||||
* createdAt: Date
|
||||
* }
|
||||
*
|
||||
* const options: QueryOptions<Product> = {
|
||||
* filters: { price: 50 },
|
||||
* sort: { sortBy: 'createdAt', order: 'desc' },
|
||||
* pagination: { page: 1, pageSize: 20 },
|
||||
* dateRange: { relativeDays: 30 }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface QueryOptions<T> {
|
||||
/** Filter configuration */
|
||||
filters?: FilterConfig<T>
|
||||
/** Sort configuration */
|
||||
sort?: SortConfig<T>
|
||||
/** Pagination settings */
|
||||
pagination?: PaginationConfig
|
||||
/** Date range filter */
|
||||
dateRange?: DateRangeConfig
|
||||
}
|
||||
|
||||
/**
|
||||
* Service method result wrapper with metadata.
|
||||
*
|
||||
* @template T - Type of the result data
|
||||
*
|
||||
* @remarks
|
||||
* Standard envelope for service responses, providing:
|
||||
* - Success/failure status
|
||||
* - Result data or error
|
||||
* - Optional metadata
|
||||
*
|
||||
* Enables consistent error handling across services.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* // Success result
|
||||
* const success: ServiceResult<User> = {
|
||||
* success: true,
|
||||
* data: { id: 1, name: 'Alice' }
|
||||
* }
|
||||
*
|
||||
* // Error result
|
||||
* const error: ServiceResult<User> = {
|
||||
* success: false,
|
||||
* error: 'User not found'
|
||||
* }
|
||||
*
|
||||
* // With metadata
|
||||
* const withMeta: ServiceResult<User[]> = {
|
||||
* success: true,
|
||||
* data: [...],
|
||||
* metadata: { cached: true, timestamp: Date.now() }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface ServiceResult<T> {
|
||||
/** Whether the operation succeeded */
|
||||
success: boolean
|
||||
/** Result data (present if success=true) */
|
||||
data?: T
|
||||
/** Error message (present if success=false) */
|
||||
error?: string
|
||||
/** Optional metadata about the operation */
|
||||
metadata?: Record<string, unknown>
|
||||
}
|
||||
|
||||
/**
|
||||
* Comparison operator for advanced filtering.
|
||||
*
|
||||
* @remarks
|
||||
* Used in complex filter expressions to specify:
|
||||
* - Equality checks
|
||||
* - Range queries
|
||||
* - Existence checks
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const op: FilterOperator = 'gte' // Greater than or equal
|
||||
* ```
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type FilterOperator = 'eq' | 'ne' | 'gt' | 'gte' | 'lt' | 'lte' | 'in' | 'nin' | 'exists'
|
||||
|
||||
/**
|
||||
* Advanced filter expression with operators.
|
||||
*
|
||||
* @template T - Value type being filtered
|
||||
*
|
||||
* @remarks
|
||||
* Enables complex query expressions beyond simple equality.
|
||||
* Similar to MongoDB query syntax.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* // Range filter
|
||||
* const ageFilter: FilterExpression<number> = {
|
||||
* operator: 'gte',
|
||||
* value: 18
|
||||
* }
|
||||
*
|
||||
* // Array inclusion filter
|
||||
* const statusFilter: FilterExpression<string> = {
|
||||
* operator: 'in',
|
||||
* value: ['active', 'pending']
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface FilterExpression<T> {
|
||||
/** Comparison operator */
|
||||
operator: FilterOperator
|
||||
/** Value to compare against */
|
||||
value: T | T[]
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue