Skip to content

Commit 889bade

Browse files
authored
feat: add templates edit command for modifying existing templates (#5)
1 parent 7c3e9ef commit 889bade

File tree

3 files changed

+124
-1
lines changed

3 files changed

+124
-1
lines changed

src/commands/templates.ts

Lines changed: 73 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
import { Command } from 'commander';
22
import { getStorage } from '../data/storage.js';
3-
import { Template, slugify, type TemplateExercise } from '../types.js';
3+
import {
4+
Template,
5+
slugify,
6+
type Template as TemplateType,
7+
type TemplateExercise,
8+
} from '../types.js';
49

510
function parseExerciseSpec(spec: string): TemplateExercise {
611
const match = spec.match(/^([^:]+):(\d+)x(.+)$/);
@@ -124,6 +129,73 @@ export function createTemplatesCommand(getProfile: () => string | undefined): Co
124129
}
125130
);
126131

132+
templates
133+
.command('edit <id>')
134+
.description('Edit an existing template')
135+
.option('-n, --name <name>', 'New template name')
136+
.option(
137+
'-e, --exercises <exercises>',
138+
'Replace exercise specs (e.g., "bench-press:3x8-12, squat:4x5")'
139+
)
140+
.option('-d, --description <description>', 'New template description')
141+
.action(
142+
(
143+
id: string,
144+
options: {
145+
name?: string;
146+
exercises?: string;
147+
description?: string;
148+
}
149+
) => {
150+
const storage = getStorage(getProfile());
151+
const existing = storage.getTemplate(id);
152+
153+
if (!existing) {
154+
console.error(`Template "${id}" not found.`);
155+
process.exit(1);
156+
}
157+
158+
const updates: Partial<TemplateType> = {};
159+
160+
if (options.name) {
161+
updates.name = options.name;
162+
}
163+
164+
if (options.description) {
165+
updates.description = options.description;
166+
}
167+
168+
if (options.exercises) {
169+
const exerciseSpecs = options.exercises.split(',').map((s) => s.trim());
170+
const exercises: TemplateExercise[] = [];
171+
172+
for (const spec of exerciseSpecs) {
173+
try {
174+
exercises.push(parseExerciseSpec(spec));
175+
} catch (err) {
176+
console.error((err as Error).message);
177+
process.exit(1);
178+
}
179+
}
180+
181+
updates.exercises = exercises;
182+
}
183+
184+
if (Object.keys(updates).length === 0) {
185+
console.error('No changes specified. Use --name, --exercises, or --description.');
186+
process.exit(1);
187+
}
188+
189+
try {
190+
storage.updateTemplate(id, updates);
191+
console.log(`Updated template: ${id}`);
192+
} catch (err) {
193+
console.error((err as Error).message);
194+
process.exit(1);
195+
}
196+
}
197+
);
198+
127199
templates
128200
.command('delete <id>')
129201
.description('Delete a template')

src/data/storage.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,16 @@ export class Storage {
159159
this.saveTemplates(templates);
160160
}
161161

162+
updateTemplate(id: string, updates: Partial<TemplateType>): void {
163+
const templates = this.getTemplates();
164+
const index = templates.findIndex((t) => t.id === id);
165+
if (index === -1) {
166+
throw new Error(`Template "${id}" not found`);
167+
}
168+
templates[index] = { ...templates[index]!, ...updates };
169+
this.saveTemplates(templates);
170+
}
171+
162172
deleteTemplate(id: string): void {
163173
const templates = this.getTemplates();
164174
const index = templates.findIndex((t) => t.id === id);

test/commands.test.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -507,6 +507,47 @@ describe('template management', () => {
507507
expect(templates.map((t) => t.id).sort()).toEqual(['legs', 'pull', 'push']);
508508
});
509509

510+
it('updates template name and description', () => {
511+
storage.addTemplate({
512+
id: 'push-a',
513+
name: 'Push A',
514+
exercises: [{ exercise: 'bench-press', sets: 3, reps: '8-12' }],
515+
});
516+
517+
storage.updateTemplate('push-a', { name: 'Push Day A', description: 'Updated push workout' });
518+
519+
const updated = storage.getTemplate('push-a')!;
520+
expect(updated.name).toBe('Push Day A');
521+
expect(updated.description).toBe('Updated push workout');
522+
expect(updated.exercises).toHaveLength(1);
523+
});
524+
525+
it('updates template exercises', () => {
526+
storage.addTemplate({
527+
id: 'pull-b',
528+
name: 'Pull B',
529+
exercises: [{ exercise: 'deadlift', sets: 3, reps: '5' }],
530+
});
531+
532+
storage.updateTemplate('pull-b', {
533+
exercises: [
534+
{ exercise: 'barbell-row', sets: 4, reps: '8-12' },
535+
{ exercise: 'lat-pulldown', sets: 3, reps: '10-15' },
536+
],
537+
});
538+
539+
const updated = storage.getTemplate('pull-b')!;
540+
expect(updated.name).toBe('Pull B');
541+
expect(updated.exercises).toHaveLength(2);
542+
expect(updated.exercises[0]?.exercise).toBe('barbell-row');
543+
});
544+
545+
it('throws when updating non-existent template', () => {
546+
expect(() => storage.updateTemplate('nonexistent', { name: 'Nope' })).toThrow(
547+
'Template "nonexistent" not found'
548+
);
549+
});
550+
510551
it('deletes template', () => {
511552
storage.addTemplate({ id: 'to-delete', name: 'Delete Me', exercises: [] });
512553
expect(storage.getTemplate('to-delete')).toBeDefined();

0 commit comments

Comments
 (0)