From f1cd45373b995ddc9fcf6f6676d53c6bd80181f2 Mon Sep 17 00:00:00 2001 From: Marsh Macy Date: Mon, 14 Jul 2025 13:05:57 -0700 Subject: [PATCH 1/2] Add JSON-based workflow profile configuration system MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Create WorkflowProfile and WorkflowRole types with predefined templates - Implement WorkflowProfileService for CRUD operations and persistence - Add WorkflowProfilesTab component for profile management UI - Support import/export of workflow profiles as JSON - Include built-in templates for common role configurations - Integrate workflow profiles tab into settings page 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- components/settings/WorkflowProfilesTab.tsx | 463 ++++++++++++++++++++ pages/SettingsPage.tsx | 18 +- services/workflowProfileService.ts | 167 +++++++ types/workflow.ts | 173 ++++++++ 4 files changed, 820 insertions(+), 1 deletion(-) create mode 100644 components/settings/WorkflowProfilesTab.tsx create mode 100644 services/workflowProfileService.ts create mode 100644 types/workflow.ts diff --git a/components/settings/WorkflowProfilesTab.tsx b/components/settings/WorkflowProfilesTab.tsx new file mode 100644 index 0000000..b10f896 --- /dev/null +++ b/components/settings/WorkflowProfilesTab.tsx @@ -0,0 +1,463 @@ +import React, { useState, useEffect } from 'react'; +import { WorkflowProfile, WorkflowRole, WORKFLOW_ROLE_TEMPLATES } from '../../types/workflow'; +import { WorkflowProfileService } from '../../services/workflowProfileService'; + +interface WorkflowProfilesTabProps { + disabled?: boolean; +} + +const WorkflowProfilesTab: React.FC = ({ disabled = false }) => { + const [profiles, setProfiles] = useState([]); + const [selectedProfile, setSelectedProfile] = useState(null); + const [isEditing, setIsEditing] = useState(false); + const [editingProfile, setEditingProfile] = useState(null); + const [importData, setImportData] = useState(''); + const [showImportModal, setShowImportModal] = useState(false); + const [error, setError] = useState(null); + + const profileService = WorkflowProfileService.getInstance(); + + useEffect(() => { + loadProfiles(); + }, []); + + const loadProfiles = () => { + try { + const loadedProfiles = profileService.getAllProfiles(); + setProfiles(loadedProfiles); + if (!selectedProfile && loadedProfiles.length > 0) { + setSelectedProfile(loadedProfiles[0]); + } + } catch (err) { + setError(err instanceof Error ? err.message : 'Failed to load profiles'); + } + }; + + const handleCreateNew = () => { + const newProfile: WorkflowProfile = { + id: `profile-${Date.now()}`, + name: 'New Workflow Profile', + description: 'Custom workflow profile', + version: '1.0.0', + roles: [{ ...WORKFLOW_ROLE_TEMPLATES.technicalWriter }], + executionOrder: ['technical-writer'], + globalSettings: { + maxIterations: 10, + timeoutMinutes: 30 + } + }; + setEditingProfile(newProfile); + setIsEditing(true); + }; + + const handleEdit = (profile: WorkflowProfile) => { + setEditingProfile({ ...profile }); + setIsEditing(true); + }; + + const handleSave = () => { + if (!editingProfile) return; + + try { + if (profiles.some(p => p.id === editingProfile.id && p !== selectedProfile)) { + // New profile + profileService.addProfile(editingProfile); + } else { + // Update existing + profileService.updateProfile(editingProfile.id, editingProfile); + } + loadProfiles(); + setIsEditing(false); + setEditingProfile(null); + setSelectedProfile(editingProfile); + setError(null); + } catch (err) { + setError(err instanceof Error ? err.message : 'Failed to save profile'); + } + }; + + const handleDelete = (profileId: string) => { + if (profiles.length <= 1) { + setError('Cannot delete the last workflow profile'); + return; + } + + try { + profileService.deleteProfile(profileId); + loadProfiles(); + if (selectedProfile?.id === profileId) { + setSelectedProfile(profiles.find(p => p.id !== profileId) || null); + } + setError(null); + } catch (err) { + setError(err instanceof Error ? err.message : 'Failed to delete profile'); + } + }; + + const handleExport = () => { + const collection = profileService.exportProfiles(); + const blob = new Blob([JSON.stringify(collection, null, 2)], { type: 'application/json' }); + const url = URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = url; + a.download = `workflow-profiles-${new Date().toISOString().split('T')[0]}.json`; + document.body.appendChild(a); + a.click(); + document.body.removeChild(a); + URL.revokeObjectURL(url); + }; + + const handleImport = () => { + try { + const collection = JSON.parse(importData); + profileService.importProfiles(collection); + loadProfiles(); + setShowImportModal(false); + setImportData(''); + setError(null); + } catch (err) { + setError(err instanceof Error ? err.message : 'Failed to import profiles'); + } + }; + + const addRole = (templateKey: keyof typeof WORKFLOW_ROLE_TEMPLATES) => { + if (!editingProfile) return; + + const template = WORKFLOW_ROLE_TEMPLATES[templateKey]; + const newRole: WorkflowRole = { + ...template, + id: `${template.id}-${Date.now()}` + }; + + setEditingProfile({ + ...editingProfile, + roles: [...editingProfile.roles, newRole], + executionOrder: [...editingProfile.executionOrder, newRole.id] + }); + }; + + const removeRole = (roleId: string) => { + if (!editingProfile) return; + + setEditingProfile({ + ...editingProfile, + roles: editingProfile.roles.filter(role => role.id !== roleId), + executionOrder: editingProfile.executionOrder.filter(id => id !== roleId) + }); + }; + + const updateRole = (roleId: string, updates: Partial) => { + if (!editingProfile) return; + + setEditingProfile({ + ...editingProfile, + roles: editingProfile.roles.map(role => + role.id === roleId ? { ...role, ...updates } : role + ) + }); + }; + + if (isEditing && editingProfile) { + return ( +
+
+

+ {profiles.some(p => p.id === editingProfile.id) ? 'Edit' : 'Create'} Workflow Profile +

+
+ + +
+
+ +
+
+ + setEditingProfile({ ...editingProfile, name: e.target.value })} + className="w-full px-3 py-2 border border-theme rounded-md bg-theme-elevated text-theme-primary" + disabled={disabled} + /> +
+ +
+ +