import * as yaml from 'js-yaml'; import { Knex } from 'knex'; import { GroupRepository, TaskRepository, StepRepository, NoteRepository } from './db/repositories'; import { Group, Task, Step, Note } from '@shared/index'; export interface YamlData { groups: YamlGroup[]; } export interface YamlGroup { name: string; tasks: YamlTask[]; } export interface YamlTask { name: string; steps: YamlStep[]; notes?: YamlNote[]; } export interface YamlStep { name: string; instructions: string; notes?: YamlNote[]; } export interface YamlNote { content: string; } export class YamlService { private groupRepo: GroupRepository; private taskRepo: TaskRepository; private stepRepo: StepRepository; private noteRepo: NoteRepository; private db: Knex; constructor(db: Knex) { this.db = db; this.groupRepo = new GroupRepository(db); this.taskRepo = new TaskRepository(db); this.stepRepo = new StepRepository(db); this.noteRepo = new NoteRepository(db); } async exportToYaml(): Promise { const groups = await this.groupRepo.findRootGroups(); const yamlData: YamlData = { groups: [] }; for (const group of groups) { const yamlGroup = await this.convertGroupToYaml(group); yamlData.groups.push(yamlGroup); } return yaml.dump(yamlData, { indent: 2, lineWidth: 120, noRefs: true }); } async importFromYaml(yamlContent: string): Promise { const yamlData = yaml.load(yamlContent) as YamlData; if (!yamlData || !yamlData.groups) { throw new Error('Invalid YAML format: missing groups'); } // Clear existing data (optional - you might want to make this configurable) await this.clearExistingData(); // Import groups and their nested data for (const yamlGroup of yamlData.groups) { await this.importGroup(yamlGroup); } } private async convertGroupToYaml(group: Group): Promise { const tasks = await this.taskRepo.findByGroupId(group.id); const yamlTasks: YamlTask[] = []; for (const task of tasks) { const yamlTask = await this.convertTaskToYaml(task); yamlTasks.push(yamlTask); } return { name: group.name, tasks: yamlTasks }; } private async convertTaskToYaml(task: Task): Promise { const steps = await this.stepRepo.findByTaskId(task.id); const notes = await this.noteRepo.findByTaskId(task.id); const yamlSteps: YamlStep[] = []; for (const step of steps) { const stepNotes = await this.noteRepo.findByStepId(step.id); const yamlStep: YamlStep = { name: step.name, instructions: step.instructions, notes: stepNotes.map(note => ({ content: note.content })) }; yamlSteps.push(yamlStep); } const yamlTask: YamlTask = { name: task.name, steps: yamlSteps, notes: notes.map(note => ({ content: note.content })) }; return yamlTask; } private async importGroup(yamlGroup: YamlGroup): Promise { // Create the group const group = await this.groupRepo.create({ name: yamlGroup.name, parent_id: undefined }); // Import tasks for this group for (const yamlTask of yamlGroup.tasks) { await this.importTask(yamlTask, group.id); } } private async importTask(yamlTask: YamlTask, groupId: number): Promise { // Create the task const task = await this.taskRepo.create({ name: yamlTask.name, group_id: groupId, print_count: 0 }); // Import task-level notes if (yamlTask.notes) { for (const yamlNote of yamlTask.notes) { await this.noteRepo.create({ content: yamlNote.content, task_id: task.id, created_by: 1 // Default user ID }); } } // Import steps for (let i = 0; i < yamlTask.steps.length; i++) { const yamlStep = yamlTask.steps[i]; await this.importStep(yamlStep, task.id, i + 1); } } private async importStep(yamlStep: YamlStep, taskId: number, order: number): Promise { // Create the step const step = await this.stepRepo.create({ name: yamlStep.name, instructions: yamlStep.instructions, task_id: taskId, order: order, print_count: 0 }); // Import step-level notes if (yamlStep.notes) { for (const yamlNote of yamlStep.notes) { await this.noteRepo.create({ content: yamlNote.content, step_id: step.id, created_by: 1 // Default user ID }); } } } private async clearExistingData(): Promise { // Delete in reverse order to respect foreign key constraints await this.db('notes').del(); await this.db('steps').del(); await this.db('tasks').del(); await this.db('groups').del(); } }