diff --git a/frontend/src/pages/StepLibraryPage.tsx b/frontend/src/pages/StepLibraryPage.tsx index 5592c7d8..532acd4d 100644 --- a/frontend/src/pages/StepLibraryPage.tsx +++ b/frontend/src/pages/StepLibraryPage.tsx @@ -1,25 +1,179 @@ -import { Bookmark } from 'lucide-react' +import { useState } from 'react' +import { Bookmark, Trash2 } from 'lucide-react' +import { useAuthStore } from '@/store/authStore' +import { usePermissions } from '@/hooks/usePermissions' +import { stepsApi } from '@/api/steps' +import { StepLibraryBrowser } from '@/components/step-library/StepLibraryBrowser' +import { StepFormModal } from '@/components/step-library/StepFormModal' +import type { Step, StepListItem } from '@/types/step' export default function StepLibraryPage() { + const user = useAuthStore((s) => s.user) + const { canCreateSteps } = usePermissions() + + // Create/edit modal state + const [createOpen, setCreateOpen] = useState(false) + const [editingStep, setEditingStep] = useState(null) + + // Delete confirmation state + const [deletingStep, setDeletingStep] = useState(null) + const [isDeleting, setIsDeleting] = useState(false) + const [deleteError, setDeleteError] = useState(null) + + // Toast for "Save to My Library" + const [saveToast, setSaveToast] = useState(null) + + // Increment to trigger StepLibraryBrowser reload + const [refreshKey, setRefreshKey] = useState(0) + const refresh = () => setRefreshKey(k => k + 1) + + // Fetch full step before opening edit modal (StepListItem lacks content) + const handleEdit = async (step: StepListItem) => { + try { + const full = await stepsApi.get(step.id) + setEditingStep(full) + } catch (err) { + console.error('Failed to load step for edit:', err) + } + } + + const handleDeleteRequest = (step: StepListItem) => { + setDeletingStep(step) + } + + const handleDeleteConfirm = async () => { + if (!deletingStep) return + setIsDeleting(true) + setDeleteError(null) + try { + await stepsApi.delete(deletingStep.id) + setDeletingStep(null) + refresh() + } catch (err) { + console.error('Failed to delete step:', err) + setDeleteError('Failed to delete step. Please try again.') + } finally { + setIsDeleting(false) + } + } + + const handleSave = async (step: StepListItem) => { + try { + const full = await stepsApi.get(step.id) + await stepsApi.create({ + title: full.title, + step_type: full.step_type, + content: full.content, + visibility: 'private', + category_id: full.category_id, + tags: full.tags, + }) + setSaveToast(`"${full.title}" saved to My Steps`) + setTimeout(() => setSaveToast(null), 3000) + refresh() + } catch (err) { + console.error('Failed to save step:', err) + } + } + + const handleFormSuccess = (_step: Step) => { + setCreateOpen(false) + setEditingStep(null) + refresh() + } + + const handleCloseModal = () => { + setCreateOpen(false) + setEditingStep(null) + } + return ( -
-
+
+ {/* Page Header */} +
- -

Step Library

+ + + +
+

Step Library

+

Reusable steps you can insert into any flow

+
-

Reusable steps for your flows — coming soon.

+ {canCreateSteps && ( + + )}
-
-
- -
-

Coming Soon

-

- The Step Library will let you create, share, and reuse common troubleshooting steps across all your flows. -

+ {/* Browser fills remaining height */} +
+ { handleEdit(step) }} + onDelete={handleDeleteRequest} + onSave={handleSave} + currentUserId={user?.id} + refreshKey={refreshKey} + showCreateButton={false} + />
+ + {/* Create / Edit Modal */} + + + {/* Delete Confirmation Dialog */} + {deletingStep && ( +
+
+
+
+ +
+

Delete Step

+
+

+ Are you sure you want to delete{' '} + "{deletingStep.title}"? +

+

This cannot be undone.

+ {deleteError && ( +

{deleteError}

+ )} +
+ + +
+
+
+ )} + + {/* Save Toast */} + {saveToast && ( +
+ {saveToast} +
+ )}
) }