POWR/types/templates.ts

214 lines
4.7 KiB
TypeScript
Raw Normal View History

2025-02-19 21:39:47 -05:00
// types/template.ts
import { BaseExercise, ExerciseCategory } from './exercise';
import { StorageSource, SyncableContent } from './shared';
import { generateId } from '@/utils/ids';
/**
* Template Classifications
*/
export type TemplateType = 'strength' | 'circuit' | 'emom' | 'amrap';
export type TemplateCategory =
| 'Full Body'
| 'Push/Pull/Legs'
| 'Upper/Lower'
| 'Custom'
| 'Cardio'
| 'CrossFit'
| 'Strength'
| 'Conditioning';
/**
* Exercise configurations within templates
*/
export interface TemplateExerciseDisplay {
title: string;
targetSets: number;
targetReps: number;
notes?: string;
}
export interface TemplateExerciseConfig {
exercise: BaseExercise;
targetSets: number;
targetReps: number;
weight?: number;
rpe?: number;
setType?: 'warmup' | 'normal' | 'drop' | 'failure';
restSeconds?: number;
notes?: string;
format?: {
weight?: boolean;
reps?: boolean;
rpe?: boolean;
set_type?: boolean;
};
format_units?: {
weight?: 'kg' | 'lbs';
reps?: 'count';
rpe?: '0-10';
set_type?: 'warmup|normal|drop|failure';
};
}
/**
* Template versioning and derivation tracking
*/
export interface TemplateSource {
id: string;
eventId?: string;
authorName?: string;
authorPubkey?: string;
version?: number;
}
/**
* Base template properties shared between UI and database
*/
export interface TemplateBase {
id: string;
title: string;
type: TemplateType;
category: TemplateCategory;
description?: string;
tags: string[];
metadata?: {
lastUsed?: number;
useCount?: number;
averageDuration?: number;
};
author?: {
name: string;
pubkey?: string;
};
}
/**
* UI Template - Used for display and interaction
*/
export interface Template extends TemplateBase {
exercises: TemplateExerciseDisplay[];
source: 'local' | 'powr' | 'nostr';
isFavorite: boolean; // Required for UI state
}
/**
* Full Template - Used for database storage and Nostr events
*/
export interface WorkoutTemplate extends TemplateBase, SyncableContent {
exercises: TemplateExerciseConfig[];
isPublic: boolean;
version: number;
// Template configuration
format?: {
weight?: boolean;
reps?: boolean;
rpe?: boolean;
set_type?: boolean;
};
format_units?: {
weight: 'kg' | 'lbs';
reps: 'count';
rpe: '0-10';
set_type: 'warmup|normal|drop|failure';
};
// Workout specific configuration
rounds?: number;
duration?: number;
interval?: number;
restBetweenRounds?: number;
// Template derivation
sourceTemplate?: TemplateSource;
derivatives?: {
count: number;
lastCreated: number;
};
// Nostr integration
nostrEventId?: string;
}
/**
* Helper Functions
*/
/**
* Gets a display string for the template source
*/
export function getSourceDisplay(template: WorkoutTemplate): string {
if (!template.sourceTemplate) {
return template.availability.source.includes('nostr')
? 'NOSTR'
: template.availability.source.includes('powr')
? 'POWR'
: 'Local Template';
}
const author = template.sourceTemplate.authorName || 'Unknown Author';
if (template.sourceTemplate.version) {
return `Modified from ${author} (v${template.sourceTemplate.version})`;
}
return `Original by ${author}`;
}
/**
* Converts a WorkoutTemplate to Template for UI display
*/
export function toTemplateDisplay(template: WorkoutTemplate): Template {
return {
id: template.id,
title: template.title,
type: template.type,
category: template.category,
description: template.description,
exercises: template.exercises.map(ex => ({
title: ex.exercise.title,
targetSets: ex.targetSets,
targetReps: ex.targetReps,
notes: ex.notes
})),
tags: template.tags,
source: template.availability.source.includes('nostr')
? 'nostr'
: template.availability.source.includes('powr')
? 'powr'
: 'local',
isFavorite: false,
metadata: template.metadata,
author: template.author
};
}
/**
* Converts a Template to WorkoutTemplate for storage/sync
*/
export function toWorkoutTemplate(template: Template): WorkoutTemplate {
return {
...template,
exercises: template.exercises.map(ex => ({
exercise: {
id: generateId(),
title: ex.title,
type: 'strength',
category: 'Push' as ExerciseCategory,
tags: [],
availability: {
source: ['local']
},
created_at: Date.now()
},
targetSets: ex.targetSets,
targetReps: ex.targetReps,
notes: ex.notes
})),
isPublic: false,
version: 1,
created_at: Date.now(),
availability: {
source: ['local']
}
};
}