mirror of
https://github.com/DocNR/POWR.git
synced 2025-04-19 19:01:18 +00:00
214 lines
4.7 KiB
TypeScript
214 lines
4.7 KiB
TypeScript
![]() |
// 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']
|
||
|
}
|
||
|
};
|
||
|
}
|