From 5ba19c097af91e3a098a057522aa5458a219982c Mon Sep 17 00:00:00 2001 From: ajaysi Date: Sun, 7 Sep 2025 08:42:37 +0530 Subject: [PATCH] Analytics Insights and Tools Modal --- .../MainDashboard/ContentLifecyclePillars.tsx | 39 +++- .../MainDashboard/MainDashboard.tsx | 126 +++++----- .../components/AnalyticsInsights.tsx | 36 ++- .../components/EnhancedTodayModal.tsx | 8 + .../MainDashboard/components/ToolsModal.tsx | 221 ++++++++++++++++++ .../src/components/shared/DashboardHeader.tsx | 4 +- .../src/components/shared/SearchFilter.tsx | 7 +- frontend/src/components/shared/types.ts | 1 + frontend/src/components/shared/utils.ts | 15 +- 9 files changed, 367 insertions(+), 90 deletions(-) create mode 100644 frontend/src/components/MainDashboard/components/ToolsModal.tsx diff --git a/frontend/src/components/MainDashboard/ContentLifecyclePillars.tsx b/frontend/src/components/MainDashboard/ContentLifecyclePillars.tsx index 6fa43585..aea7507e 100644 --- a/frontend/src/components/MainDashboard/ContentLifecyclePillars.tsx +++ b/frontend/src/components/MainDashboard/ContentLifecyclePillars.tsx @@ -290,21 +290,48 @@ const PillarCard: React.FC<{ onMouseEnter={() => setIsHovered(true)} onMouseLeave={() => setIsHovered(false)} > - + {/* Shooting star border animation */} + + {/* Header */} - + - + {pillar.title} @@ -378,7 +405,7 @@ const PillarCard: React.FC<{ transition={{ duration: 0.3, ease: 'easeInOut' }} style={{ overflow: 'hidden' }} > - + {pillar.id === 'plan' ? ( <> { // State to track if we need to start a newly generated workflow const [shouldStartWorkflow, setShouldStartWorkflow] = React.useState(false); + // Tools Modal state + const [toolsModalOpen, setToolsModalOpen] = React.useState(false); + const [modalCategoryName, setModalCategoryName] = React.useState(null); + const [modalCategory, setModalCategory] = React.useState(null); + const [searchResults, setSearchResults] = React.useState([]); + // Handle workflow start const handleStartWorkflow = async () => { try { @@ -170,6 +177,56 @@ const MainDashboard: React.FC = () => { showSnackbar(`Launching ${tool.name}...`, 'info'); }; + // Handle category click to open modal + const handleCategoryClick = (categoryName: string | null, categoryData?: any) => { + setModalCategoryName(categoryName); + setModalCategory(categoryData); + setToolsModalOpen(true); + }; + + // Handle search to show results in modal with debouncing + React.useEffect(() => { + if (searchQuery && searchQuery.length >= 2) { // Only search after 2+ characters + const timeoutId = setTimeout(() => { + // Get all tools from all categories that match search + const allTools: Tool[] = []; + Object.values(toolCategories).forEach(category => { + if (category) { + const tools = getToolsForCategory(category, null); + allTools.push(...tools); + } + }); + + const filtered = allTools.filter(tool => + tool.name.toLowerCase().includes(searchQuery.toLowerCase()) || + tool.description.toLowerCase().includes(searchQuery.toLowerCase()) || + tool.features.some(feature => feature.toLowerCase().includes(searchQuery.toLowerCase())) + ); + + setSearchResults(filtered); + setModalCategoryName(null); + setModalCategory(null); + setToolsModalOpen(true); + }, 500); // 500ms delay + + return () => clearTimeout(timeoutId); + } else if (searchQuery && searchQuery.length < 2) { + // Close modal if search query is too short + setToolsModalOpen(false); + } + }, [searchQuery, toolCategories]); + + // Close modal and clear search + const handleCloseModal = () => { + setToolsModalOpen(false); + setModalCategoryName(null); + setModalCategory(null); + setSearchResults([]); + if (searchQuery) { + setSearchQuery(''); + } + }; + const filteredCategories = getFilteredCategories( toolCategories, selectedCategory, @@ -245,9 +302,6 @@ const MainDashboard: React.FC = () => { {/* Content Lifecycle Pillars - First Panel */} - {/* Analytics Insights - Good/Bad/Ugly */} - - {/* Search and Filter */} { onSubCategoryChange={setSelectedSubCategory} toolCategories={toolCategories} theme={theme} + onCategoryClick={handleCategoryClick} /> - {/* Enhanced Tools Grid */} - - {Object.entries(filteredCategories).map(([categoryName, category], categoryIndex) => ( - - - {/* Show Category Header when no specific category is selected OR when searching across all categories */} - {(selectedCategory === null || searchQuery) && ( - - )} + {/* Analytics Insights - Good/Bad/Ugly */} + - - {getToolsForCategory(category, selectedSubCategory).map((tool: Tool, toolIndex: number) => ( - - - - - - ))} - - - - ))} - - - {/* Empty State */} - {Object.keys(filteredCategories).length === 0 && ( - 🔍} - title="No tools found matching your criteria" - message="Try adjusting your search or category filter" - onClearFilters={clearFilters} - clearButtonText="Clear Filters" - /> - )} + {/* Tools Modal */} + diff --git a/frontend/src/components/MainDashboard/components/AnalyticsInsights.tsx b/frontend/src/components/MainDashboard/components/AnalyticsInsights.tsx index 0d4b7205..a52f72d7 100644 --- a/frontend/src/components/MainDashboard/components/AnalyticsInsights.tsx +++ b/frontend/src/components/MainDashboard/components/AnalyticsInsights.tsx @@ -92,11 +92,6 @@ const Badge = styled('span')(({ theme }) => ({ fontSize: '0.65rem' })); -// Subtle shimmer animation for the title text -const shimmerText = keyframes` - 0% { background-position: -200% 0; } - 100% { background-position: 200% 0; } -`; const mockData: AnalyticsData = { theGood: [ @@ -268,23 +263,20 @@ const AnalyticsInsights: React.FC = ({ data, onActionCli }; return ( - - - Analytics Insights - + + + + Today's Analytics Insights + + {columns.map((col) => { const isHovered = hovered === col.key; diff --git a/frontend/src/components/MainDashboard/components/EnhancedTodayModal.tsx b/frontend/src/components/MainDashboard/components/EnhancedTodayModal.tsx index 709ac945..46bf5c77 100644 --- a/frontend/src/components/MainDashboard/components/EnhancedTodayModal.tsx +++ b/frontend/src/components/MainDashboard/components/EnhancedTodayModal.tsx @@ -128,19 +128,27 @@ const EnhancedTodayModal: React.FC = ({ }; const handleWorkflowComplete = async () => { + console.log('Workflow Complete clicked for pillar:', pillarId); + console.log('Current pillar tasks:', pillarTasks); + // Mark all remaining tasks in this pillar as completed const incompleteTasks = pillarTasks.filter(task => task.status !== 'completed' && task.status !== 'skipped' ); + console.log('Incomplete tasks to complete:', incompleteTasks); + for (const task of incompleteTasks) { try { + console.log('Completing task:', task.id); await completeTask(task.id); + console.log('Task completed successfully:', task.id); } catch (error) { console.error(`Failed to complete task ${task.id}:`, error); } } + console.log('All tasks completed, closing modal'); // Close the modal onClose(); }; diff --git a/frontend/src/components/MainDashboard/components/ToolsModal.tsx b/frontend/src/components/MainDashboard/components/ToolsModal.tsx new file mode 100644 index 00000000..742aa918 --- /dev/null +++ b/frontend/src/components/MainDashboard/components/ToolsModal.tsx @@ -0,0 +1,221 @@ +import React from 'react'; +import { + Box, + Modal, + Typography, + IconButton, + Grid, + Stack, + Chip, + Divider +} from '@mui/material'; +import { motion, AnimatePresence } from 'framer-motion'; +import { + Close as CloseIcon, + Search as SearchIcon +} from '@mui/icons-material'; +import ToolCard from '../../shared/ToolCard'; +import { Tool } from '../../shared/types'; +import { getToolsForCategory } from '../../shared/utils'; + +interface ToolsModalProps { + open: boolean; + onClose: () => void; + categoryName?: string; + category?: any; + searchQuery?: string; + searchResults?: Tool[]; + onToolClick: (tool: Tool) => void; + favorites: string[]; + onToggleFavorite: (toolName: string) => void; +} + +const ToolsModal: React.FC = ({ + open, + onClose, + categoryName, + category, + searchQuery, + searchResults, + onToolClick, + favorites, + onToggleFavorite +}) => { + const isSearchMode = !!searchQuery; + + // Handle different modes: search, all tools, or specific category + let tools: Tool[] = []; + if (isSearchMode) { + tools = searchResults || []; + } else if (categoryName === null) { + // All Tools mode - get tools from all categories + const allTools: Tool[] = []; + if (category && typeof category === 'object') { + // category is the entire toolCategories object + Object.values(category).forEach((cat: any) => { + if (cat && typeof cat === 'object') { + // Check if this is a valid category with tools or subCategories + if ('tools' in cat || 'subCategories' in cat) { + const categoryTools = getToolsForCategory(cat, null); + if (categoryTools && Array.isArray(categoryTools)) { + allTools.push(...categoryTools); + } + } + } + }); + } + tools = allTools; + } else { + // Specific category mode + const categoryTools = getToolsForCategory(category || null, null); + tools = categoryTools && Array.isArray(categoryTools) ? categoryTools : []; + } + + // Ensure tools is always an array + if (!Array.isArray(tools)) { + tools = []; + } + + const title = isSearchMode ? `Search Results for "${searchQuery}"` : categoryName || 'All Tools'; + const subtitle = isSearchMode ? `${tools.length} tools found` : `${tools.length} tools available`; + + return ( + + + + {/* Header */} + + + {isSearchMode ? ( + + ) : categoryName === null ? ( + + ) : ( + category?.icon && ( + + {category.icon} + + ) + )} + + + {title} + + + {subtitle} + + + + + + + + + {/* Content */} + + {tools.length === 0 ? ( + + + {isSearchMode ? 'No tools found' : 'No tools available'} + + + {isSearchMode + ? 'Try adjusting your search terms or browse categories' + : 'This category is currently empty' + } + + + ) : ( + + + {tools.map((tool: Tool, index: number) => ( + + + + + + ))} + + + )} + + + + + ); +}; + +export default ToolsModal; diff --git a/frontend/src/components/shared/DashboardHeader.tsx b/frontend/src/components/shared/DashboardHeader.tsx index ae214698..333bdd3f 100644 --- a/frontend/src/components/shared/DashboardHeader.tsx +++ b/frontend/src/components/shared/DashboardHeader.tsx @@ -171,7 +171,9 @@ const DashboardHeader: React.FC = ({ width: '100%', height: '100%', background: 'linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.6), transparent)', - animation: 'shimmer 2.5s infinite', + animation: workflowControls.completedTasks === workflowControls.totalTasks + ? 'none' + : 'shimmer 2.5s infinite', zIndex: 1, }, '&::after': { diff --git a/frontend/src/components/shared/SearchFilter.tsx b/frontend/src/components/shared/SearchFilter.tsx index ec212d01..afec3083 100644 --- a/frontend/src/components/shared/SearchFilter.tsx +++ b/frontend/src/components/shared/SearchFilter.tsx @@ -24,7 +24,8 @@ const SearchFilter: React.FC = ({ selectedSubCategory, onSubCategoryChange, toolCategories, - theme + theme, + onCategoryClick }) => { // Helper function to get tool count from a category const getToolCount = (category: any): number => { @@ -103,7 +104,7 @@ const SearchFilter: React.FC = ({ onCategoryChange(null)} + onClick={() => onCategoryClick ? onCategoryClick(null, toolCategories) : onCategoryChange(null)} active={selectedCategory === null} theme={theme} toolCount={Object.values(toolCategories).reduce((total, category) => total + getToolCount(category), 0)} @@ -122,7 +123,7 @@ const SearchFilter: React.FC = ({ > onCategoryChange(category)} + onClick={() => onCategoryClick ? onCategoryClick(category, cat) : onCategoryChange(category)} active={selectedCategory === category} theme={theme} toolCount={getToolCount(cat)} diff --git a/frontend/src/components/shared/types.ts b/frontend/src/components/shared/types.ts index e7fc6969..61eeb7bb 100644 --- a/frontend/src/components/shared/types.ts +++ b/frontend/src/components/shared/types.ts @@ -73,6 +73,7 @@ export interface SearchFilterProps { onSubCategoryChange: (subCategory: string | null) => void; toolCategories: ToolCategories; theme: any; + onCategoryClick?: (category: string | null, categoryData?: any) => void; } export interface DashboardHeaderProps { diff --git a/frontend/src/components/shared/utils.ts b/frontend/src/components/shared/utils.ts index ea94f5a0..b20abcfd 100644 --- a/frontend/src/components/shared/utils.ts +++ b/frontend/src/components/shared/utils.ts @@ -1,19 +1,26 @@ import { Category, Tool, ToolCategories } from './types'; // Utility functions for dashboard components -export const getToolsForCategory = (category: Category, selectedSubCategory: string | null): Tool[] => { +export const getToolsForCategory = (category: Category | null, selectedSubCategory: string | null): Tool[] => { + if (!category) { + return []; + } + if ('subCategories' in category) { if (selectedSubCategory && category.subCategories[selectedSubCategory]) { - return category.subCategories[selectedSubCategory].tools; + const subCategory = category.subCategories[selectedSubCategory]; + return subCategory && subCategory.tools ? subCategory.tools : []; } // When no subcategory is selected, return all tools from all subcategories const allTools: Tool[] = []; Object.values(category.subCategories).forEach(subCategory => { - allTools.push(...subCategory.tools); + if (subCategory && subCategory.tools && Array.isArray(subCategory.tools)) { + allTools.push(...subCategory.tools); + } }); return allTools; } - return category.tools; + return category.tools && Array.isArray(category.tools) ? category.tools : []; }; export const getFilteredCategories = (