Nagesh Muralidhar
Initial commit of PodCraft application
fd52f31
import React, { useState, useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import { FaPlus, FaCalendarAlt, FaEdit, FaTrash, FaPlayCircle, FaRegClock, FaFlask, FaMicrophone, FaHeadphones, FaPodcast, FaCheck } from 'react-icons/fa';
import { MdOutlineWorkspaces } from 'react-icons/md';
import { TiFlowMerge } from 'react-icons/ti';
import './Workflows.css';
const Workflows = () => {
const navigate = useNavigate();
const [workflows, setWorkflows] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
const [deleteConfirmId, setDeleteConfirmId] = useState(null);
useEffect(() => {
fetchWorkflows();
}, []);
const fetchWorkflows = async () => {
try {
const token = localStorage.getItem('token');
const response = await fetch('http://localhost:8000/api/workflows', {
headers: {
'Authorization': `Bearer ${token}`
}
});
if (!response.ok) {
throw new Error('Failed to fetch workflows');
}
const data = await response.json();
setWorkflows(data);
setLoading(false);
} catch (err) {
console.error('Error fetching workflows:', err);
setError(err.message);
setLoading(false);
}
};
const handleWorkflowClick = (workflowId) => {
navigate(`/workflows/workflow/${workflowId}`);
};
const handleCreateWorkflow = () => {
// Use -1 to indicate a new workflow
navigate(`/workflows/workflow/-1`);
};
const handleDeleteClick = (e, workflowId) => {
e.stopPropagation(); // Prevent workflow card click
setDeleteConfirmId(workflowId);
};
const handleDeleteConfirm = async (e, workflowId) => {
e.stopPropagation(); // Prevent workflow card click
try {
const token = localStorage.getItem('token');
const response = await fetch(`http://localhost:8000/api/workflows/${workflowId}`, {
method: 'DELETE',
headers: {
'Authorization': `Bearer ${token}`
}
});
if (!response.ok) {
throw new Error('Failed to delete workflow');
}
// Remove from state
setWorkflows(workflows.filter(w => w.id !== workflowId));
setDeleteConfirmId(null);
} catch (err) {
console.error('Error deleting workflow:', err);
setError(err.message);
}
};
const handleDeleteCancel = (e) => {
e.stopPropagation(); // Prevent workflow card click
setDeleteConfirmId(null);
};
const getRandomGradient = () => {
const gradients = [
'linear-gradient(135deg, #6366F1, #8B5CF6)',
'linear-gradient(135deg, #3B82F6, #6366F1)',
'linear-gradient(135deg, #10B981, #3B82F6)',
'linear-gradient(135deg, #F59E0B, #10B981)',
'linear-gradient(135deg, #EF4444, #F59E0B)',
'linear-gradient(135deg, #EC4899, #8B5CF6)',
'linear-gradient(135deg, #8B5CF6, #EC4899)',
'linear-gradient(135deg, #6366F1, #EC4899)'
];
return gradients[Math.floor(Math.random() * gradients.length)];
};
const formatDate = (dateString) => {
const date = new Date(dateString);
return new Intl.DateTimeFormat('en-US', {
month: 'short',
day: 'numeric',
year: 'numeric'
}).format(date);
};
if (loading) {
return (
<div className="workflows-container">
<div className="loading-indicator">
<div className="loader"></div>
<span>Loading your workflows...</span>
</div>
</div>
);
}
if (error) {
return (
<div className="workflows-container">
<div className="error-message">
<span>⚠️ Error: {error}</span>
</div>
</div>
);
}
return (
<div className="workflows-container">
<div className="workflows-header">
<div className="header-content">
<TiFlowMerge className="title-icon" />
<h1>Your Podcast Workflows</h1>
</div>
<button className="create-workflow-btn" onClick={handleCreateWorkflow}>
<FaPlus /> New Workflow
</button>
</div>
<div className="workflows-subheader">
<p>Manage and edit your podcast workflow templates</p>
<div className="workflows-stats">
<div className="stat-item">
<MdOutlineWorkspaces />
<span>{workflows.length} Workflows</span>
</div>
</div>
</div>
<div className="workflows-grid">
{workflows.length === 0 ? (
<div className="no-workflows">
<FaPodcast className="empty-icon" />
<h3>No Workflows Yet</h3>
<p>You haven't created any workflows yet. Click the "New Workflow" button to get started!</p>
<button className="create-workflow-empty-btn" onClick={handleCreateWorkflow}>
<FaPlus /> Create Your First Workflow
</button>
</div>
) : (
workflows.map((workflow) => (
<div
key={workflow.id}
className="workflow-card"
onClick={() => handleWorkflowClick(workflow.id)}
style={{ '--card-gradient': getRandomGradient() }}
>
<div className="workflow-card-header">
<div className="workflow-icon">
<TiFlowMerge />
</div>
<div className="workflow-actions">
{deleteConfirmId === workflow.id ? (
<div className="delete-confirm">
<button
className="delete-yes"
onClick={(e) => handleDeleteConfirm(e, workflow.id)}
title="Confirm delete"
>
<FaCheck />
</button>
<button
className="delete-no"
onClick={handleDeleteCancel}
title="Cancel"
>
<FaEdit />
</button>
</div>
) : (
<button
className="delete-btn"
onClick={(e) => handleDeleteClick(e, workflow.id)}
title="Delete workflow"
>
<FaTrash />
</button>
)}
</div>
</div>
<h3>{workflow.name}</h3>
<p className="workflow-description">{workflow.description || 'No description available'}</p>
<div className="workflow-meta">
<div className="meta-item">
<FaCalendarAlt />
<span>{formatDate(workflow.created_at)}</span>
</div>
<div className="meta-item">
<FaRegClock />
<span>Last edited: {formatDate(workflow.updated_at || workflow.created_at)}</span>
</div>
</div>
<div className="workflow-card-footer">
<div className="workflow-status">
{workflow.insights ? (
<span className="status ready">
<FaHeadphones /> Ready
</span>
) : (
<span className="status draft">
<FaFlask /> Draft
</span>
)}
</div>
<button className="open-btn">
<FaPlayCircle /> Open
</button>
</div>
</div>
))
)}
</div>
</div>
);
};
export default Workflows;