From e29bacf3aeb5b3e6a68439891479443031e069e1 Mon Sep 17 00:00:00 2001 From: Patel230 Date: Fri, 22 Aug 2025 02:36:45 +0530 Subject: [PATCH 01/51] Enhance UI consistency and text wrapping across all views - Implement responsive text wrapping for titles and descriptions in Card, Kanban, and Table views using break-words and table-text-content CSS - Standardize status icons across Card and Kanban views (PLANNED uses Calendar, IN_PROGRESS uses Clock) - Unify missing due date handling with consistent 'No due date' + 'Schedule recommended' messages - Remove 'Due Date' label from Card view while preserving date functionality - Add comprehensive GraphQL integration with proper cache management for real-time updates - Enhance node menu with description display and improved truncation - Implement auto-refresh indicators and improved error handling - Add CSS utilities for professional text wrapping that maintains original layout dimensions --- packages/server/src/schema/neo4j-schema.ts | 12 +- packages/server/src/scripts/seed.ts | 12 +- .../web/src/components/CreateNodeModal.tsx | 78 +++-- .../web/src/components/DeleteNodeModal.tsx | 30 +- packages/web/src/components/EditNodeModal.tsx | 39 ++- .../web/src/components/GraphVisualization.tsx | 30 +- packages/web/src/components/ListView.tsx | 287 ++++++++++++------ packages/web/src/index.css | 10 + 8 files changed, 354 insertions(+), 144 deletions(-) diff --git a/packages/server/src/schema/neo4j-schema.ts b/packages/server/src/schema/neo4j-schema.ts index 53e5f661..1d795c3f 100644 --- a/packages/server/src/schema/neo4j-schema.ts +++ b/packages/server/src/schema/neo4j-schema.ts @@ -182,9 +182,19 @@ export const typeDefs = gql` } enum EdgeType { - DEPENDENCY + DEPENDS_ON BLOCKS + ENABLES RELATES_TO + PART_OF + FOLLOWS + PARALLEL_WITH + DUPLICATES + CONFLICTS_WITH + VALIDATES + + # Legacy support (deprecated) + DEPENDENCY CONTAINS } diff --git a/packages/server/src/scripts/seed.ts b/packages/server/src/scripts/seed.ts index a20405f2..7c8349f9 100644 --- a/packages/server/src/scripts/seed.ts +++ b/packages/server/src/scripts/seed.ts @@ -116,12 +116,12 @@ async function seed() { // Create edges (relationships between work items) const edges = [ - { source: 'wi-1', target: 'wi-2', type: 'DEPENDENCY' }, - { source: 'wi-2', target: 'wi-3', type: 'DEPENDENCY' }, - { source: 'wi-5', target: 'wi-6', type: 'CONTAINS' }, - { source: 'wi-5', target: 'wi-7', type: 'CONTAINS' }, - { source: 'wi-5', target: 'wi-8', type: 'CONTAINS' }, - { source: 'wi-12', target: 'wi-5', type: 'DEPENDENCY' } + { source: 'wi-1', target: 'wi-2', type: 'DEPENDS_ON' }, + { source: 'wi-2', target: 'wi-3', type: 'DEPENDS_ON' }, + { source: 'wi-5', target: 'wi-6', type: 'PART_OF' }, + { source: 'wi-5', target: 'wi-7', type: 'PART_OF' }, + { source: 'wi-5', target: 'wi-8', type: 'PART_OF' }, + { source: 'wi-12', target: 'wi-5', type: 'DEPENDS_ON' } ]; for (const edge of edges) { diff --git a/packages/web/src/components/CreateNodeModal.tsx b/packages/web/src/components/CreateNodeModal.tsx index 49eba7b0..01a7f929 100644 --- a/packages/web/src/components/CreateNodeModal.tsx +++ b/packages/web/src/components/CreateNodeModal.tsx @@ -59,42 +59,78 @@ export function CreateNodeModal({ isOpen, onClose, parentNodeId, position }: Cre const [createWorkItem, { loading: creatingWorkItem }] = useMutation(CREATE_WORK_ITEM, { - refetchQueries: [{ - query: GET_WORK_ITEMS, - variables: { - where: { - teamId: currentTeam?.id || 'default-team' + refetchQueries: [ + { + query: GET_WORK_ITEMS, + variables: { + options: { limit: 100 } + } + }, + { + query: GET_WORK_ITEMS, + variables: { + where: { + teamId: currentTeam?.id || 'team-1' + } } } - }], + ], awaitRefetchQueries: true, update: (cache, { data }) => { // Update Apollo cache for immediate UI refresh if (data?.createWorkItems?.workItems) { const newNode = data.createWorkItems.workItems[0]; - // Update existing cached query - const existingData = cache.readQuery({ - query: GET_WORK_ITEMS, - variables: { - where: { - teamId: currentTeam?.id || 'default-team' + // Update cache for GraphVisualization (no team filter) + try { + const graphData = cache.readQuery({ + query: GET_WORK_ITEMS, + variables: { + options: { limit: 100 } } + }) as { workItems: any[] } | null; + + if (graphData) { + cache.writeQuery({ + query: GET_WORK_ITEMS, + variables: { + options: { limit: 100 } + }, + data: { + workItems: [newNode, ...graphData.workItems] + } + }); } - }) as { workItems: any[] } | null; - - if (existingData) { - cache.writeQuery({ + } catch { + // Silently fail if cache read fails + } + + // Update cache for ListView (with team filter) + try { + const listData = cache.readQuery({ query: GET_WORK_ITEMS, variables: { where: { - teamId: currentTeam?.id || 'default-team' + teamId: currentTeam?.id || 'team-1' } - }, - data: { - workItems: [newNode, ...existingData.workItems] } - }); + }) as { workItems: any[] } | null; + + if (listData) { + cache.writeQuery({ + query: GET_WORK_ITEMS, + variables: { + where: { + teamId: currentTeam?.id || 'team-1' + } + }, + data: { + workItems: [newNode, ...listData.workItems] + } + }); + } + } catch { + // Silently fail if cache read fails } } } diff --git a/packages/web/src/components/DeleteNodeModal.tsx b/packages/web/src/components/DeleteNodeModal.tsx index 83f09cbb..e4c5b4ac 100644 --- a/packages/web/src/components/DeleteNodeModal.tsx +++ b/packages/web/src/components/DeleteNodeModal.tsx @@ -17,23 +17,41 @@ export function DeleteNodeModal({ isOpen, onClose, nodeId, nodeTitle, nodeType } const { showSuccess, showError } = useNotifications(); const [deleteWorkItem, { loading: deletingNode }] = useMutation(DELETE_WORK_ITEM, { - refetchQueries: [{ - query: GET_WORK_ITEMS, - variables: { - where: { - teamId: currentTeam?.id || 'default-team' + refetchQueries: [ + { + query: GET_WORK_ITEMS, + variables: { + options: { limit: 100 } } + }, + { + query: GET_WORK_ITEMS, + variables: { + where: { + teamId: currentTeam?.id || 'team-1' + } + } + } + ], + awaitRefetchQueries: true, + update: (cache, { data }) => { + if (data?.deleteWorkItems?.nodesDeleted > 0) { + // Evict all workItems queries to ensure complete refresh across all views + cache.evict({ fieldName: 'workItems' }); + cache.gc(); } - }], + } }); const handleDelete = async () => { try { + await deleteWorkItem({ variables: { where: { id: nodeId } } }); + showSuccess( 'Node Deleted Successfully!', diff --git a/packages/web/src/components/EditNodeModal.tsx b/packages/web/src/components/EditNodeModal.tsx index 8a72467b..46a70fb4 100644 --- a/packages/web/src/components/EditNodeModal.tsx +++ b/packages/web/src/components/EditNodeModal.tsx @@ -66,14 +66,33 @@ export function EditNodeModal({ isOpen, onClose, node }: EditNodeModalProps) { const isFormValid = formData.title.trim() !== '' && formData.type !== ''; const [updateWorkItem, { loading: updatingNode }] = useMutation(UPDATE_WORK_ITEM, { - refetchQueries: [{ - query: GET_WORK_ITEMS, - variables: { - where: { - teamId: currentTeam?.id || 'default-team' - } + // Smart cache updates instead of aggressive network fetching + refetchQueries: [ + { + query: GET_WORK_ITEMS, + variables: { options: { limit: 100 } } + }, + { + query: GET_WORK_ITEMS, + variables: { where: { teamId: currentTeam?.id || 'team-1' } } + } + ], + awaitRefetchQueries: true, + notifyOnNetworkStatusChange: true, + update: (cache, { data }) => { + // Force cache invalidation for immediate UI updates across all views + if (data?.updateWorkItems?.workItems) { + // Invalidate all workItems cache to force UI refresh + cache.evict({ fieldName: 'workItems' }); + cache.gc(); + + // Also evict root query to ensure complete refresh + cache.evict({ + id: 'ROOT_QUERY', + fieldName: 'workItems' + }); } - }], + } }); React.useEffect(() => { @@ -119,7 +138,6 @@ export function EditNodeModal({ isOpen, onClose, node }: EditNodeModalProps) { priorityComp: (formData.priorityExec + formData.priorityIndiv + formData.priorityComm) / 3, }; - console.log('Updating node with input:', updateInput); const result = await updateWorkItem({ variables: { @@ -137,6 +155,11 @@ export function EditNodeModal({ isOpen, onClose, node }: EditNodeModalProps) { ); onClose(); + } else { + showError( + 'Update Failed', + 'The node update did not return valid data. Please try again.' + ); } } catch (error) { console.error('Error updating node:', error); diff --git a/packages/web/src/components/GraphVisualization.tsx b/packages/web/src/components/GraphVisualization.tsx index c7e09e64..39beaeaa 100644 --- a/packages/web/src/components/GraphVisualization.tsx +++ b/packages/web/src/components/GraphVisualization.tsx @@ -8,6 +8,7 @@ import { CreateNodeModal } from './CreateNodeModal'; interface WorkItem { id: string; title: string; + description?: string; type: string; priorityComp: number; positionX: number; @@ -58,14 +59,22 @@ export function GraphVisualization() { const { data: workItemsData, loading: workItemsLoading } = useQuery(GET_WORK_ITEMS, { variables: { options: { limit: 100 } - } + }, + fetchPolicy: 'cache-and-network', // Use cache first, then fetch from network for updates + notifyOnNetworkStatusChange: true, + errorPolicy: 'all' }); - const { data: edgesData, loading: edgesLoading } = useQuery(GET_EDGES); + const { data: edgesData, loading: edgesLoading } = useQuery(GET_EDGES, { + fetchPolicy: 'cache-and-network', + notifyOnNetworkStatusChange: true + }); const workItems: WorkItem[] = workItemsData?.workItems || []; const edges: Edge[] = edgesData?.edges || []; + + // Convert dependency relationships to edges for visualization const dependencyEdges: Edge[] = workItems.flatMap(item => (item.dependencies || []).map((dep: any) => ({ @@ -306,6 +315,14 @@ export function GraphVisualization() {
+ {/* Auto-refresh indicator */} + {loading && ( +
+
+ Auto-refreshing... +
+ )} + {/* Legend */}

Node Types

@@ -357,8 +374,15 @@ export function GraphVisualization() { onClick={(e) => e.stopPropagation()} >
-
{nodeMenu.workItem.title}
+
+ {nodeMenu.workItem.title} +
{nodeMenu.workItem.type}
+ {nodeMenu.workItem.description && ( +
+ {nodeMenu.workItem.description} +
+ )}
-

{node.title}

+

{node.title}

{node.description && ( -

{node.description}

+

{node.description}

)} {/* Priority and Due Date */}
Priority - Due Date
{/* Priority - Left Side */} @@ -419,38 +457,37 @@ export function ListView() {
= 0.8 ? 'bg-red-500' : - node.priority.computed >= 0.6 ? 'bg-orange-500' : - node.priority.computed >= 0.4 ? 'bg-yellow-500' : - node.priority.computed >= 0.2 ? 'bg-blue-500' : 'bg-green-500' - }`} style={{ height: `${Math.max(node.priority.computed * 100, 8)}%` }}>
+ getNodePriority(node) >= 0.8 ? 'bg-red-500' : + getNodePriority(node) >= 0.6 ? 'bg-orange-500' : + getNodePriority(node) >= 0.4 ? 'bg-yellow-500' : + getNodePriority(node) >= 0.2 ? 'bg-blue-500' : 'bg-green-500' + }`} style={{ height: `${Math.max(getNodePriority(node) * 100, 8)}%` }}>
= 0.8 ? 'text-red-500' : - node.priority.computed >= 0.6 ? 'text-orange-500' : - node.priority.computed >= 0.4 ? 'text-yellow-500' : - node.priority.computed >= 0.2 ? 'text-blue-500' : 'text-green-500' + getNodePriority(node) >= 0.8 ? 'text-red-500' : + getNodePriority(node) >= 0.6 ? 'text-orange-500' : + getNodePriority(node) >= 0.4 ? 'text-yellow-500' : + getNodePriority(node) >= 0.2 ? 'text-blue-500' : 'text-green-500' }`}> - {Math.round(node.priority.computed * 100)}% + {Math.round(getNodePriority(node) * 100)}% = 0.8 ? 'text-red-500' : - node.priority.computed >= 0.6 ? 'text-orange-500' : - node.priority.computed >= 0.4 ? 'text-yellow-500' : - node.priority.computed >= 0.2 ? 'text-blue-500' : + getNodePriority(node) >= 0.8 ? 'text-red-500' : + getNodePriority(node) >= 0.6 ? 'text-orange-500' : + getNodePriority(node) >= 0.4 ? 'text-yellow-500' : + getNodePriority(node) >= 0.2 ? 'text-blue-500' : 'text-green-500' }`}> - {node.priority.computed >= 0.8 ? 'Critical' : - node.priority.computed >= 0.6 ? 'High' : - node.priority.computed >= 0.4 ? 'Medium' : - node.priority.computed >= 0.2 ? 'Low' : 'Minimal'} + {getNodePriority(node) >= 0.8 ? 'Critical' : + getNodePriority(node) >= 0.6 ? 'High' : + getNodePriority(node) >= 0.4 ? 'Medium' : + getNodePriority(node) >= 0.2 ? 'Low' : 'Minimal'}
- {/* Due Date - Right Side */}
{node.dueDate ? (
@@ -495,8 +532,13 @@ export function ListView() {
) : ( -
- No date +
+
+ No due date +
+
+ Schedule recommended +
)}
@@ -507,8 +549,8 @@ export function ListView() {
{/* Contributor */}
- {node.contributor ? ( - getContributorAvatar(node.contributor) + {node.assignedTo ? ( + getContributorAvatar(node.assignedTo) ) : (
@@ -558,7 +600,7 @@ export function ListView() { }, 'PLANNED': { label: 'Planned', - icon: , + icon: , color: 'bg-purple-500', bgColor: 'bg-gray-750', textColor: 'text-purple-400', @@ -567,7 +609,7 @@ export function ListView() { }, 'IN_PROGRESS': { label: 'In Progress', - icon: , + icon: , color: 'bg-yellow-500', bgColor: 'bg-gray-750', textColor: 'text-yellow-400', @@ -597,7 +639,7 @@ export function ListView() { const nodesByStatus = statuses.reduce((acc, status) => { acc[status] = filteredNodes.filter(node => node.status === status); return acc; - }, {} as Record); + }, {} as Record); return (
@@ -641,7 +683,7 @@ export function ListView() { e.stopPropagation(); handleEditNode(node); }} - className="flex items-center justify-center w-8 h-8 rounded-full bg-blue-50 hover:bg-blue-100 dark:bg-blue-900/20 dark:hover:bg-blue-900/30 text-blue-600 dark:text-blue-400 transition-colors" + className="flex items-center justify-center w-6 h-6 rounded-full bg-blue-50 hover:bg-blue-100 dark:bg-blue-900/20 dark:hover:bg-blue-900/30 text-blue-600 dark:text-blue-400 transition-colors" title="Edit node" > @@ -659,7 +701,14 @@ export function ListView() {
-

{node.title}

+

{node.title}

+ + {/* Description */} + {node.description && ( +

+ {node.description} +

+ )} {/* Priority and Due Date */}
@@ -667,22 +716,22 @@ export function ListView() {
= 0.8 ? 'bg-red-500' : - node.priority.computed >= 0.6 ? 'bg-orange-500' : - node.priority.computed >= 0.4 ? 'bg-yellow-500' : - node.priority.computed >= 0.2 ? 'bg-blue-500' : 'bg-green-500' - }`} style={{ height: `${Math.max(node.priority.computed * 100, 5)}%` }}>
+ getNodePriority(node) >= 0.8 ? 'bg-red-500' : + getNodePriority(node) >= 0.6 ? 'bg-orange-500' : + getNodePriority(node) >= 0.4 ? 'bg-yellow-500' : + getNodePriority(node) >= 0.2 ? 'bg-blue-500' : 'bg-green-500' + }`} style={{ height: `${Math.max(getNodePriority(node) * 100, 5)}%` }}>
= 0.8 ? 'text-red-500' : - node.priority.computed >= 0.6 ? 'text-orange-500' : - node.priority.computed >= 0.4 ? 'text-yellow-500' : - node.priority.computed >= 0.2 ? 'text-blue-500' : 'text-green-500' + getNodePriority(node) >= 0.8 ? 'text-red-500' : + getNodePriority(node) >= 0.6 ? 'text-orange-500' : + getNodePriority(node) >= 0.4 ? 'text-yellow-500' : + getNodePriority(node) >= 0.2 ? 'text-blue-500' : 'text-green-500' }`} style={{ - bottom: `${Math.max(node.priority.computed * 100, 5)}%`, + bottom: `${Math.max(getNodePriority(node) * 100, 5)}%`, transform: 'translateY(50%)' }}> - {Math.round(node.priority.computed * 100)}% + {Math.round(getNodePriority(node) * 100)}%
@@ -731,14 +780,19 @@ export function ListView() {
) : ( -
- No due date +
+
+ No due date +
+
+ Schedule recommended +
)}
- {node.contributor && getContributorAvatar(node.contributor)} + {node.assignedTo && getContributorAvatar(node.assignedTo)} ))} @@ -797,11 +851,11 @@ export function ListView() { {filteredNodes.map((node) => ( - - + +
-
{node.title}
+
{node.title}
{/* Action buttons - appear on hover */}
{node.description && ( -
{node.description}
+
{node.description}
)} + {/* Tags not available in WorkItem schema - commented out for now {node.tags && node.tags.length > 0 && (
{node.tags.map((tag, index) => ( @@ -841,15 +896,15 @@ export function ListView() { ))}
- )} + )} */}
- + {formatLabel(node.type)} - +
- - {node.contributor ? ( - getContributorAvatar(node.contributor) + + {node.assignedTo ? ( + getContributorAvatar(node.assignedTo) ) : (
@@ -882,40 +937,40 @@ export function ListView() {
)} - +
= 0.8 ? 'bg-red-500' : - node.priority.computed >= 0.6 ? 'bg-orange-500' : - node.priority.computed >= 0.4 ? 'bg-yellow-500' : - node.priority.computed >= 0.2 ? 'bg-blue-500' : + getNodePriority(node) >= 0.8 ? 'bg-red-500' : + getNodePriority(node) >= 0.6 ? 'bg-orange-500' : + getNodePriority(node) >= 0.4 ? 'bg-yellow-500' : + getNodePriority(node) >= 0.2 ? 'bg-blue-500' : 'bg-green-500' }`} style={{ - height: `${Math.max(node.priority.computed * 100, 5)}%` + height: `${Math.max(getNodePriority(node) * 100, 5)}%` }} >
= 0.8 ? 'text-red-500' : - node.priority.computed >= 0.6 ? 'text-orange-500' : - node.priority.computed >= 0.4 ? 'text-yellow-500' : - node.priority.computed >= 0.2 ? 'text-blue-500' : + getNodePriority(node) >= 0.8 ? 'text-red-500' : + getNodePriority(node) >= 0.6 ? 'text-orange-500' : + getNodePriority(node) >= 0.4 ? 'text-yellow-500' : + getNodePriority(node) >= 0.2 ? 'text-blue-500' : 'text-green-500' }`} style={{ - bottom: `${Math.max(node.priority.computed * 100, 5)}%`, + bottom: `${Math.max(getNodePriority(node) * 100, 5)}%`, transform: 'translateY(50%)' }} > - {Math.round(node.priority.computed * 100)}% + {Math.round(getNodePriority(node) * 100)}%
- + {node.dueDate ? (
option.id === currentView)!; + // Show loading state while fetching data + if (loading && !data) { + return ( +
+
+
+

Loading work items...

+
+
+ ); + } + + // Show error state if data fetch failed + if (error) { + return ( +
+
+

Error loading work items

+

{error.message}

+ +
+
+ ); + } + if (!currentGraph) { return (
@@ -1473,7 +1558,9 @@ export function ListView() {
+ {/* Tags */} +
+ + setFormData(prev => ({ ...prev, tags }))} + maxTags={5} + /> +
+ {/* Contributor and Due Date Row */}
diff --git a/packages/web/src/components/EditNodeModal.tsx b/packages/web/src/components/EditNodeModal.tsx index 46a70fb4..ae82344a 100644 --- a/packages/web/src/components/EditNodeModal.tsx +++ b/packages/web/src/components/EditNodeModal.tsx @@ -5,6 +5,7 @@ import { UPDATE_WORK_ITEM, GET_WORK_ITEMS } from '../lib/queries'; import { useAuth } from '../contexts/AuthContext'; import { useNotifications } from '../contexts/NotificationContext'; import { NodeTypeSelector } from './NodeCategorySelector'; +import { TagInput } from './TagInput'; interface EditNodeModalProps { isOpen: boolean; @@ -18,6 +19,9 @@ interface EditNodeModalProps { priorityExec: number; priorityIndiv: number; priorityComm: number; + tags?: string[]; + dueDate?: string; + assignedTo?: string; }; } @@ -25,6 +29,16 @@ export function EditNodeModal({ isOpen, onClose, node }: EditNodeModalProps) { const { currentTeam } = useAuth(); const { showSuccess, showError } = useNotifications(); + // Helper function to format DateTime to date string (YYYY-MM-DD) + const formatDateForInput = (dateTime?: string) => { + if (!dateTime) return ''; + try { + const date = new Date(dateTime); + return date.toISOString().split('T')[0]; // Extract YYYY-MM-DD part + } catch { + return ''; + } + }; const [formData, setFormData] = React.useState({ title: node.title, @@ -34,8 +48,9 @@ export function EditNodeModal({ isOpen, onClose, node }: EditNodeModalProps) { priorityExec: node.priorityExec || 0, priorityIndiv: node.priorityIndiv || 0, priorityComm: node.priorityComm || 0, - assignedTo: '', - dueDate: '', + assignedTo: node.assignedTo || '', + dueDate: formatDateForInput(node.dueDate), + tags: node.tags || [], }); const [isStatusOpen, setIsStatusOpen] = React.useState(false); @@ -105,8 +120,9 @@ export function EditNodeModal({ isOpen, onClose, node }: EditNodeModalProps) { priorityExec: node.priorityExec || 0, priorityIndiv: node.priorityIndiv || 0, priorityComm: node.priorityComm || 0, - assignedTo: '', - dueDate: '', + assignedTo: node.assignedTo || '', + dueDate: formatDateForInput(node.dueDate), + tags: node.tags || [], }); } }, [isOpen, node]); @@ -131,6 +147,7 @@ export function EditNodeModal({ isOpen, onClose, node }: EditNodeModalProps) { priorityComm: formData.priorityComm, assignedTo: formData.assignedTo || undefined, dueDate: formData.dueDate || undefined, + tags: formData.tags || [], }; const updateInput = { @@ -316,7 +333,17 @@ export function EditNodeModal({ isOpen, onClose, node }: EditNodeModalProps) { />
- {/* Contributor and Due Date Row */} +
+ + setFormData(prev => ({ ...prev, tags }))} + maxTags={5} + /> +
+
@@ -1957,7 +2004,7 @@ export function ListView() { setStatusFilter('All Statuses'); setContributorFilter('All Contributors'); setPriorityFilter('All Priorities'); - }} + }} className="text-green-400 text-sm hover:text-green-300 mt-2" > Clear all filters diff --git a/packages/web/src/components/TagDisplay.tsx b/packages/web/src/components/TagDisplay.tsx new file mode 100644 index 00000000..845b1ef0 --- /dev/null +++ b/packages/web/src/components/TagDisplay.tsx @@ -0,0 +1,43 @@ +import { Tag } from 'lucide-react'; + +interface TagDisplayProps { + tags?: string[] | null; + className?: string; + compact?: boolean; +} + +export function TagDisplay({ tags, className = '', compact = false }: TagDisplayProps) { + if (!tags || tags.length === 0) return null; + + // Same color scheme as TagInput for consistency + const tagColors = [ + { bg: 'bg-blue-500 dark:bg-blue-600', text: 'text-white' }, + { bg: 'bg-green-500 dark:bg-green-600', text: 'text-white' }, + { bg: 'bg-purple-500 dark:bg-purple-600', text: 'text-white' }, + { bg: 'bg-orange-500 dark:bg-orange-600', text: 'text-white' }, + { bg: 'bg-pink-500 dark:bg-pink-600', text: 'text-white' }, + ]; + + const getTagColor = (index: number) => { + return tagColors[index % tagColors.length]; + }; + + return ( +
+ {tags.map((tag, index) => { + const color = getTagColor(index); + return ( + + {!compact && } + {tag} + + ); + })} +
+ ); +} \ No newline at end of file diff --git a/packages/web/src/components/TagInput.tsx b/packages/web/src/components/TagInput.tsx new file mode 100644 index 00000000..e2ea8d70 --- /dev/null +++ b/packages/web/src/components/TagInput.tsx @@ -0,0 +1,154 @@ +import React, { useState, KeyboardEvent } from 'react'; +import { X, Plus, Tag } from 'lucide-react'; + +interface TagInputProps { + tags: string[]; + onChange: (tags: string[]) => void; + placeholder?: string; + maxTags?: number; + disabled?: boolean; +} + +export function TagInput({ + tags, + onChange, + placeholder = "Type and press comma to add tags", + maxTags = 5, + disabled = false +}: TagInputProps) { + const [inputValue, setInputValue] = useState(''); + const [isInputFocused, setIsInputFocused] = useState(false); + + // High contrast color schemes for better visibility + const tagColors = [ + { bg: 'bg-blue-500 dark:bg-blue-600', border: 'border-blue-600 dark:border-blue-500', text: 'text-white' }, + { bg: 'bg-green-500 dark:bg-green-600', border: 'border-green-600 dark:border-green-500', text: 'text-white' }, + { bg: 'bg-purple-500 dark:bg-purple-600', border: 'border-purple-600 dark:border-purple-500', text: 'text-white' }, + { bg: 'bg-orange-500 dark:bg-orange-600', border: 'border-orange-600 dark:border-orange-500', text: 'text-white' }, + { bg: 'bg-pink-500 dark:bg-pink-600', border: 'border-pink-600 dark:border-pink-500', text: 'text-white' }, + ]; + + const getTagColor = (index: number) => { + return tagColors[index % tagColors.length]; + }; + + const addTags = (input: string) => { + // Split by comma and process multiple tags + const newTags = input.split(',') + .map(tag => tag.trim()) + .filter(tag => tag.length > 0 && tag.length <= 30); + + const uniqueNewTags = newTags.filter(tag => !tags.includes(tag)); + const availableSlots = maxTags - tags.length; + const tagsToAdd = uniqueNewTags.slice(0, availableSlots); + + if (tagsToAdd.length > 0) { + onChange([...tags, ...tagsToAdd]); + setInputValue(''); + } + }; + + const removeTag = (tagToRemove: string) => { + onChange(tags.filter(tag => tag !== tagToRemove)); + }; + + const handleKeyDown = (e: KeyboardEvent) => { + if (e.key === 'Enter' && inputValue.trim()) { + e.preventDefault(); + addTags(inputValue); + } else if (e.key === 'Backspace' && !inputValue && tags.length > 0) { + // Remove last tag if input is empty and backspace is pressed + removeTag(tags[tags.length - 1]); + } else if (e.key === 'Escape') { + setInputValue(''); + } + }; + + const handleInputChange = (e: React.ChangeEvent) => { + const value = e.target.value; + setInputValue(value); + + // Auto-add tags when comma is typed + if (value.includes(',')) { + addTags(value); + } + }; + + const handleAddClick = () => { + if (inputValue.trim()) { + addTags(inputValue); + } + }; + + return ( +
+
+ {/* Existing tags */} + {tags.map((tag, index) => { + const color = getTagColor(index); + return ( + + + {tag} + {!disabled && ( + + )} + + ); + })} + + {/* Input field */} + {!disabled && tags.length < maxTags && ( +
+ setIsInputFocused(true)} + onBlur={() => setIsInputFocused(false)} + placeholder={tags.length === 0 ? placeholder : "Continue typing..."} + className="flex-1 outline-none bg-transparent text-gray-900 dark:text-white text-sm min-w-0" + maxLength={100} + /> + {inputValue.trim() && ( + + )} +
+ )} +
+ + {/* Helper text */} +
+ + Use comma (,) to separate multiple tags + + + {tags.length} of {maxTags} + +
+
+ ); +} \ No newline at end of file diff --git a/packages/web/src/index.css b/packages/web/src/index.css index de267c46..1d5faa99 100644 --- a/packages/web/src/index.css +++ b/packages/web/src/index.css @@ -180,4 +180,55 @@ svg text:not(.completion-indicator) { .priority-low { @apply text-green-600; +} + +/* Dark theme date picker styling */ +input[type="date"] { + color-scheme: dark; +} + +/* WebKit/Chromium browsers */ +input[type="date"]::-webkit-calendar-picker-indicator { + background-color: #374151; + border-radius: 4px; + border: 1px solid #4b5563; + padding: 2px; + cursor: pointer; + filter: invert(1); +} + +input[type="date"]::-webkit-calendar-picker-indicator:hover { + background-color: #4b5563; + border-color: #6b7280; +} + +/* Firefox */ +input[type="date"]::-moz-calendar-picker-indicator { + background-color: #374151; + border-radius: 4px; + border: 1px solid #4b5563; + padding: 2px; + cursor: pointer; +} + +/* Dark theme for the actual calendar popup */ +@supports (color-scheme: dark) { + input[type="date"] { + color-scheme: dark; + } +} + +/* Force dark theme on date inputs in dark mode */ +.dark input[type="date"], +html.dark input[type="date"], +input[type="date"].dark-theme { + color-scheme: dark; + background-color: #374151; + border-color: #4b5563; + color: #f9fafb; +} + +/* Additional styling for better contrast */ +input[type="date"]:focus { + color-scheme: dark; } \ No newline at end of file diff --git a/packages/web/src/lib/queries.ts b/packages/web/src/lib/queries.ts index 03716872..fd367a2a 100644 --- a/packages/web/src/lib/queries.ts +++ b/packages/web/src/lib/queries.ts @@ -20,6 +20,7 @@ export const GET_WORK_ITEMS = gql` priorityComp dueDate assignedTo + tags teamId userId contributors { @@ -95,6 +96,7 @@ export const GET_WORK_ITEM_BY_ID = gql` priorityComp dueDate assignedTo + tags contributors { id name @@ -137,6 +139,7 @@ export const CREATE_WORK_ITEM = gql` priorityComp dueDate assignedTo + tags createdAt } } @@ -164,6 +167,7 @@ export const UPDATE_WORK_ITEM = gql` priorityComp dueDate assignedTo + tags updatedAt } } From 09810fb9471f9b1a0c45b2a46b71bc292f948046 Mon Sep 17 00:00:00 2001 From: Patel230 Date: Fri, 22 Aug 2025 18:30:53 +0530 Subject: [PATCH 03/51] Update UI consistency across ListView components - Updated status icons and colors to match CreateNodeModal/EditNodeModal - Changed PROPOSED status from Lightbulb to ClipboardList icon - Updated all status colors to use -400 variants for consistency - Synchronized status styling across table, card, kanban, and dashboard views - Added new node types (IDEA, OUTCOME, RESEARCH) with proper icons and colors - Updated priority display with larger icons (h-6 w-6) and consistent colors - Improved filter dropdown sizing and spacing for better UX - Enhanced Project Overview Task Status section with consistent styling --- .../web/src/components/CreateNodeModal.tsx | 82 ++++----- .../web/src/components/DeleteNodeModal.tsx | 4 +- packages/web/src/components/EditNodeModal.tsx | 76 ++++----- packages/web/src/components/ListView.tsx | 160 +++++++----------- .../src/components/NodeCategorySelector.tsx | 79 ++++++--- packages/web/src/components/TagInput.tsx | 17 +- 6 files changed, 210 insertions(+), 208 deletions(-) diff --git a/packages/web/src/components/CreateNodeModal.tsx b/packages/web/src/components/CreateNodeModal.tsx index e60f5e9a..13fe2edd 100644 --- a/packages/web/src/components/CreateNodeModal.tsx +++ b/packages/web/src/components/CreateNodeModal.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { useMutation } from '@apollo/client'; -import { X, Link, Lightbulb, Calendar, Clock, CheckCircle, AlertCircle, ChevronDown, Flame, Zap, Triangle, Circle, ArrowDown } from 'lucide-react'; +import { X, Link, Lightbulb, Calendar, Clock, CheckCircle, AlertCircle, ChevronDown, Flame, Zap, Triangle, Circle, ArrowDown, Workflow, FileText, Send, Plus, Star, ClipboardList } from 'lucide-react'; import { CREATE_WORK_ITEM, GET_WORK_ITEMS } from '../lib/queries'; import { useAuth } from '../contexts/AuthContext'; import { useNotifications } from '../contexts/NotificationContext'; @@ -36,11 +36,11 @@ export function CreateNodeModal({ isOpen, onClose, parentNodeId, position }: Cre // Status options with icons const statusOptions = [ - { value: 'PROPOSED', label: 'Proposed', icon: , color: 'text-blue-600' }, - { value: 'PLANNED', label: 'Planned', icon: , color: 'text-purple-600' }, - { value: 'IN_PROGRESS', label: 'In Progress', icon: , color: 'text-yellow-600' }, - { value: 'COMPLETED', label: 'Completed', icon: , color: 'text-green-600' }, - { value: 'BLOCKED', label: 'Blocked', icon: , color: 'text-red-600' } + { value: 'PROPOSED', label: 'Proposed', icon: , color: 'text-cyan-400' }, + { value: 'PLANNED', label: 'Planned', icon: , color: 'text-purple-400' }, + { value: 'IN_PROGRESS', label: 'In Progress', icon: , color: 'text-yellow-400' }, + { value: 'COMPLETED', label: 'Completed', icon: , color: 'text-green-400' }, + { value: 'BLOCKED', label: 'Blocked', icon: , color: 'text-red-400' } ]; // Close status dropdown when clicking outside @@ -277,7 +277,7 @@ export function CreateNodeModal({ isOpen, onClose, parentNodeId, position }: Cre value={formData.title} onChange={(e) => setFormData(prev => ({ ...prev, title: e.target.value }))} className="w-full border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-900 dark:text-white rounded-lg px-3 py-2 focus:ring-2 focus:ring-blue-500 focus:border-transparent" - placeholder="Enter node title..." + placeholder="Enter node title" />
@@ -289,7 +289,7 @@ export function CreateNodeModal({ isOpen, onClose, parentNodeId, position }: Cre setFormData(prev => ({ ...prev, type }))} - placeholder="Select node type..." + placeholder="Select node type" />
@@ -314,7 +314,7 @@ export function CreateNodeModal({ isOpen, onClose, parentNodeId, position }: Cre {selectedStatus.label} ) : ( - Select status... + Select status ); })()}
@@ -375,7 +375,7 @@ export function CreateNodeModal({ isOpen, onClose, parentNodeId, position }: Cre value={formData.description} onChange={(e) => setFormData(prev => ({ ...prev, description: e.target.value }))} className="w-full border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-900 dark:text-white rounded-lg px-3 py-2 focus:ring-2 focus:ring-blue-500 focus:border-transparent" - placeholder="Describe the node..." + placeholder="Describe the node" />
@@ -451,8 +451,8 @@ export function CreateNodeModal({ isOpen, onClose, parentNodeId, position }: Cre className="bg-gray-50 dark:bg-gray-700 rounded-lg p-2 border border-red-500/30 text-center hover:shadow-sm hover:bg-gray-100 dark:hover:bg-gray-600 transition-all cursor-pointer" >
- -
Critical
+ +
Critical
80% - 100%
@@ -470,8 +470,8 @@ export function CreateNodeModal({ isOpen, onClose, parentNodeId, position }: Cre className="bg-gray-50 dark:bg-gray-700 rounded-lg p-2 border border-orange-500/30 text-center hover:shadow-sm hover:bg-gray-100 dark:hover:bg-gray-600 transition-all cursor-pointer" >
- -
High
+ +
High
60% - 79%
@@ -489,8 +489,8 @@ export function CreateNodeModal({ isOpen, onClose, parentNodeId, position }: Cre className="bg-gray-50 dark:bg-gray-700 rounded-lg p-2 border border-yellow-500/30 text-center hover:shadow-sm hover:bg-gray-100 dark:hover:bg-gray-600 transition-all cursor-pointer" >
- -
Moderate
+ +
Moderate
40% - 59%
@@ -511,8 +511,8 @@ export function CreateNodeModal({ isOpen, onClose, parentNodeId, position }: Cre className="bg-gray-50 dark:bg-gray-700 rounded-lg p-2 border border-blue-500/30 text-center hover:shadow-sm hover:bg-gray-100 dark:hover:bg-gray-600 transition-all cursor-pointer" >
- -
Low
+ +
Low
20% - 39%
@@ -530,8 +530,8 @@ export function CreateNodeModal({ isOpen, onClose, parentNodeId, position }: Cre className="bg-gray-50 dark:bg-gray-700 rounded-lg p-2 border border-green-500/30 text-center hover:shadow-sm hover:bg-gray-100 dark:hover:bg-gray-600 transition-all cursor-pointer" >
- -
Minimal
+ +
Minimal
0% - 19%
@@ -561,7 +561,7 @@ export function CreateNodeModal({ isOpen, onClose, parentNodeId, position }: Cre 'accent-green-500' }`} /> -
= 0.8 ? 'text-red-500' : formData.priorityExec >= 0.6 ? 'text-orange-500' : formData.priorityExec >= 0.4 ? 'text-yellow-500' : @@ -569,15 +569,15 @@ export function CreateNodeModal({ isOpen, onClose, parentNodeId, position }: Cre 'text-green-500' }`}> {formData.priorityExec >= 0.8 ? ( - <>Critical + <>Critical ) : formData.priorityExec >= 0.6 ? ( - <>High + <>High ) : formData.priorityExec >= 0.4 ? ( - <>Moderate + <>Moderate ) : formData.priorityExec >= 0.2 ? ( - <>Low + <>Low ) : ( - <>Minimal + <>Minimal )} ({Math.round(formData.priorityExec * 100)}%)
@@ -604,7 +604,7 @@ export function CreateNodeModal({ isOpen, onClose, parentNodeId, position }: Cre 'accent-green-500' }`} /> -
= 0.8 ? 'text-red-500' : formData.priorityIndiv >= 0.6 ? 'text-orange-500' : formData.priorityIndiv >= 0.4 ? 'text-yellow-500' : @@ -612,15 +612,15 @@ export function CreateNodeModal({ isOpen, onClose, parentNodeId, position }: Cre 'text-green-500' }`}> {formData.priorityIndiv >= 0.8 ? ( - <>Critical + <>Critical ) : formData.priorityIndiv >= 0.6 ? ( - <>High + <>High ) : formData.priorityIndiv >= 0.4 ? ( - <>Moderate + <>Moderate ) : formData.priorityIndiv >= 0.2 ? ( - <>Low + <>Low ) : ( - <>Minimal + <>Minimal )} ({Math.round(formData.priorityIndiv * 100)}%)
@@ -647,7 +647,7 @@ export function CreateNodeModal({ isOpen, onClose, parentNodeId, position }: Cre 'accent-green-500' }`} /> -
= 0.8 ? 'text-red-500' : formData.priorityComm >= 0.6 ? 'text-orange-500' : formData.priorityComm >= 0.4 ? 'text-yellow-500' : @@ -655,15 +655,15 @@ export function CreateNodeModal({ isOpen, onClose, parentNodeId, position }: Cre 'text-green-500' }`}> {formData.priorityComm >= 0.8 ? ( - <>Critical + <>Critical ) : formData.priorityComm >= 0.6 ? ( - <>High + <>High ) : formData.priorityComm >= 0.4 ? ( - <>Moderate + <>Moderate ) : formData.priorityComm >= 0.2 ? ( - <>Low + <>Low ) : ( - <>Minimal + <>Minimal )} ({Math.round(formData.priorityComm * 100)}%)
@@ -673,20 +673,20 @@ export function CreateNodeModal({ isOpen, onClose, parentNodeId, position }: Cre diff --git a/packages/web/src/components/DeleteNodeModal.tsx b/packages/web/src/components/DeleteNodeModal.tsx index e4c5b4ac..938d75f6 100644 --- a/packages/web/src/components/DeleteNodeModal.tsx +++ b/packages/web/src/components/DeleteNodeModal.tsx @@ -140,7 +140,7 @@ export function DeleteNodeModal({ isOpen, onClose, nodeId, nodeTitle, nodeType } @@ -148,7 +148,7 @@ export function DeleteNodeModal({ isOpen, onClose, nodeId, nodeTitle, nodeType } type="button" onClick={handleDelete} disabled={deletingNode} - className="px-4 py-2 text-sm font-medium text-white bg-red-600 hover:bg-red-700 disabled:bg-red-400 dark:bg-red-500 dark:hover:bg-red-600 dark:disabled:bg-red-400 rounded-lg transition-colors flex items-center space-x-2" + className="px-4 py-2 text-base font-medium text-white bg-red-700 hover:bg-red-800 disabled:bg-red-400 dark:bg-red-600 dark:hover:bg-red-700 dark:disabled:bg-red-400 rounded-lg transition-colors flex items-center space-x-2" > {deletingNode ? 'Deleting...' : 'Delete Node'} diff --git a/packages/web/src/components/EditNodeModal.tsx b/packages/web/src/components/EditNodeModal.tsx index ae82344a..805fe837 100644 --- a/packages/web/src/components/EditNodeModal.tsx +++ b/packages/web/src/components/EditNodeModal.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { useMutation } from '@apollo/client'; -import { X, Edit, Save, Lightbulb, Calendar, Clock, CheckCircle, AlertCircle, ChevronDown, Flame, Zap, Triangle, Circle, ArrowDown } from 'lucide-react'; +import { X, Edit, Save, Lightbulb, Calendar, Clock, CheckCircle, AlertCircle, ChevronDown, Flame, Zap, Triangle, Circle, ArrowDown, ClipboardList } from 'lucide-react'; import { UPDATE_WORK_ITEM, GET_WORK_ITEMS } from '../lib/queries'; import { useAuth } from '../contexts/AuthContext'; import { useNotifications } from '../contexts/NotificationContext'; @@ -58,11 +58,11 @@ export function EditNodeModal({ isOpen, onClose, node }: EditNodeModalProps) { // Status options with icons const statusOptions = [ - { value: 'PROPOSED', label: 'Proposed', icon: , color: 'text-blue-600' }, - { value: 'PLANNED', label: 'Planned', icon: , color: 'text-purple-600' }, - { value: 'IN_PROGRESS', label: 'In Progress', icon: , color: 'text-yellow-600' }, - { value: 'COMPLETED', label: 'Completed', icon: , color: 'text-green-600' }, - { value: 'BLOCKED', label: 'Blocked', icon: , color: 'text-red-600' } + { value: 'PROPOSED', label: 'Proposed', icon: , color: 'text-cyan-400' }, + { value: 'PLANNED', label: 'Planned', icon: , color: 'text-purple-400' }, + { value: 'IN_PROGRESS', label: 'In Progress', icon: , color: 'text-yellow-400' }, + { value: 'COMPLETED', label: 'Completed', icon: , color: 'text-green-400' }, + { value: 'BLOCKED', label: 'Blocked', icon: , color: 'text-red-400' } ]; // Close status dropdown when clicking outside @@ -401,8 +401,8 @@ export function EditNodeModal({ isOpen, onClose, node }: EditNodeModalProps) { className="bg-gray-50 dark:bg-gray-700 rounded-lg p-2 border border-red-500/30 text-center hover:shadow-sm hover:bg-gray-100 dark:hover:bg-gray-600 transition-all cursor-pointer" >
- -
Critical
+ +
Critical
80% - 100%
@@ -420,8 +420,8 @@ export function EditNodeModal({ isOpen, onClose, node }: EditNodeModalProps) { className="bg-gray-50 dark:bg-gray-700 rounded-lg p-2 border border-orange-500/30 text-center hover:shadow-sm hover:bg-gray-100 dark:hover:bg-gray-600 transition-all cursor-pointer" >
- -
High
+ +
High
60% - 79%
@@ -439,8 +439,8 @@ export function EditNodeModal({ isOpen, onClose, node }: EditNodeModalProps) { className="bg-gray-50 dark:bg-gray-700 rounded-lg p-2 border border-yellow-500/30 text-center hover:shadow-sm hover:bg-gray-100 dark:hover:bg-gray-600 transition-all cursor-pointer" >
- -
Moderate
+ +
Moderate
40% - 59%
@@ -460,8 +460,8 @@ export function EditNodeModal({ isOpen, onClose, node }: EditNodeModalProps) { className="bg-gray-50 dark:bg-gray-700 rounded-lg p-2 border border-blue-500/30 text-center hover:shadow-sm hover:bg-gray-100 dark:hover:bg-gray-600 transition-all cursor-pointer" >
- -
Low
+ +
Low
20% - 39%
@@ -479,8 +479,8 @@ export function EditNodeModal({ isOpen, onClose, node }: EditNodeModalProps) { className="bg-gray-50 dark:bg-gray-700 rounded-lg p-2 border border-green-500/30 text-center hover:shadow-sm hover:bg-gray-100 dark:hover:bg-gray-600 transition-all cursor-pointer" >
- -
Minimal
+ +
Minimal
0% - 19%
@@ -510,7 +510,7 @@ export function EditNodeModal({ isOpen, onClose, node }: EditNodeModalProps) { 'accent-green-500' }`} /> -
= 0.8 ? 'text-red-500' : formData.priorityExec >= 0.6 ? 'text-orange-500' : formData.priorityExec >= 0.4 ? 'text-yellow-500' : @@ -518,15 +518,15 @@ export function EditNodeModal({ isOpen, onClose, node }: EditNodeModalProps) { 'text-green-500' }`}> {formData.priorityExec >= 0.8 ? ( - <>Critical + <>Critical ) : formData.priorityExec >= 0.6 ? ( - <>High + <>High ) : formData.priorityExec >= 0.4 ? ( - <>Moderate + <>Moderate ) : formData.priorityExec >= 0.2 ? ( - <>Low + <>Low ) : ( - <>Minimal + <>Minimal )} ({Math.round(formData.priorityExec * 100)}%)
@@ -553,7 +553,7 @@ export function EditNodeModal({ isOpen, onClose, node }: EditNodeModalProps) { 'accent-green-500' }`} /> -
= 0.8 ? 'text-red-500' : formData.priorityIndiv >= 0.6 ? 'text-orange-500' : formData.priorityIndiv >= 0.4 ? 'text-yellow-500' : @@ -561,15 +561,15 @@ export function EditNodeModal({ isOpen, onClose, node }: EditNodeModalProps) { 'text-green-500' }`}> {formData.priorityIndiv >= 0.8 ? ( - <>Critical + <>Critical ) : formData.priorityIndiv >= 0.6 ? ( - <>High + <>High ) : formData.priorityIndiv >= 0.4 ? ( - <>Moderate + <>Moderate ) : formData.priorityIndiv >= 0.2 ? ( - <>Low + <>Low ) : ( - <>Minimal + <>Minimal )} ({Math.round(formData.priorityIndiv * 100)}%)
@@ -596,7 +596,7 @@ export function EditNodeModal({ isOpen, onClose, node }: EditNodeModalProps) { 'accent-green-500' }`} /> -
= 0.8 ? 'text-red-500' : formData.priorityComm >= 0.6 ? 'text-orange-500' : formData.priorityComm >= 0.4 ? 'text-yellow-500' : @@ -604,15 +604,15 @@ export function EditNodeModal({ isOpen, onClose, node }: EditNodeModalProps) { 'text-green-500' }`}> {formData.priorityComm >= 0.8 ? ( - <>Critical + <>Critical ) : formData.priorityComm >= 0.6 ? ( - <>High + <>High ) : formData.priorityComm >= 0.4 ? ( - <>Moderate + <>Moderate ) : formData.priorityComm >= 0.2 ? ( - <>Low + <>Low ) : ( - <>Minimal + <>Minimal )} ({Math.round(formData.priorityComm * 100)}%)
@@ -622,21 +622,21 @@ export function EditNodeModal({ isOpen, onClose, node }: EditNodeModalProps) { diff --git a/packages/web/src/components/ListView.tsx b/packages/web/src/components/ListView.tsx index 52d972eb..a364f5a8 100644 --- a/packages/web/src/components/ListView.tsx +++ b/packages/web/src/components/ListView.tsx @@ -22,7 +22,10 @@ import { Sparkles, ListTodo, Trophy, - AlertTriangle + AlertTriangle, + Target, + Microscope, + ClipboardList } from 'lucide-react'; import { useQuery } from '@apollo/client'; import { useAuth } from '../contexts/AuthContext'; @@ -130,31 +133,34 @@ export function ListView() { // Type options with icons const typeOptions = [ { value: 'All Types', label: 'All Types', icon: null, color: 'text-gray-400' }, - { value: 'EPIC', label: 'Epic', icon: , color: 'text-purple-600' }, - { value: 'FEATURE', label: 'Feature', icon: , color: 'text-blue-600' }, - { value: 'TASK', label: 'Task', icon: , color: 'text-green-600' }, - { value: 'BUG', label: 'Bug', icon: , color: 'text-red-600' }, - { value: 'MILESTONE', label: 'Milestone', icon: , color: 'text-orange-600' } + { value: 'EPIC', label: 'Epic', icon: , color: 'text-fuchsia-400' }, + { value: 'MILESTONE', label: 'Milestone', icon: , color: 'text-orange-400' }, + { value: 'OUTCOME', label: 'Outcome', icon: , color: 'text-indigo-400' }, + { value: 'FEATURE', label: 'Feature', icon: , color: 'text-sky-400' }, + { value: 'TASK', label: 'Task', icon: , color: 'text-green-400' }, + { value: 'BUG', label: 'Bug', icon: , color: 'text-red-400' }, + { value: 'IDEA', label: 'Idea', icon: , color: 'text-yellow-300' }, + { value: 'RESEARCH', label: 'Research', icon: , color: 'text-teal-400' } ]; // Status options with icons const statusOptions = [ { value: 'All Statuses', label: 'All Statuses', icon: null, color: 'text-gray-400' }, - { value: 'PROPOSED', label: 'Proposed', icon: , color: 'text-blue-600' }, - { value: 'PLANNED', label: 'Planned', icon: , color: 'text-purple-600' }, - { value: 'IN_PROGRESS', label: 'In Progress', icon: , color: 'text-yellow-600' }, - { value: 'COMPLETED', label: 'Completed', icon: , color: 'text-green-600' }, - { value: 'BLOCKED', label: 'Blocked', icon: , color: 'text-red-600' } + { value: 'PROPOSED', label: 'Proposed', icon: , color: 'text-cyan-400' }, + { value: 'PLANNED', label: 'Planned', icon: , color: 'text-purple-400' }, + { value: 'IN_PROGRESS', label: 'In Progress', icon: , color: 'text-yellow-400' }, + { value: 'COMPLETED', label: 'Completed', icon: , color: 'text-green-400' }, + { value: 'BLOCKED', label: 'Blocked', icon: , color: 'text-red-400' } ]; // Priority options with icons const priorityOptions = [ { value: 'All Priorities', label: 'All Priorities', icon: null, color: 'text-gray-400' }, - { value: 'Critical', label: 'Critical Priority', icon: , color: 'text-red-600' }, - { value: 'High', label: 'High Priority', icon: , color: 'text-orange-600' }, - { value: 'Moderate', label: 'Moderate Priority', icon: , color: 'text-yellow-600' }, - { value: 'Low', label: 'Low Priority', icon: , color: 'text-blue-600' }, - { value: 'Minimal', label: 'Minimal Priority', icon: , color: 'text-green-600' } + { value: 'Critical', label: 'Critical', icon: , color: 'text-red-500' }, + { value: 'High', label: 'High', icon: , color: 'text-orange-500' }, + { value: 'Moderate', label: 'Moderate', icon: , color: 'text-yellow-500' }, + { value: 'Low', label: 'Low', icon: , color: 'text-blue-500' }, + { value: 'Minimal', label: 'Minimal', icon: , color: 'text-green-500' } ]; @@ -343,56 +349,14 @@ export function ListView() { const getNodeTypeColor = (type: string) => { switch (type) { - // Strategic Planning case 'EPIC': return 'bg-purple-500 text-white'; - case 'PROJECT': return 'bg-purple-600 text-white'; - case 'MILESTONE': return 'bg-yellow-500 text-black'; - case 'GOAL': return 'bg-purple-400 text-white'; - - // Development Work - case 'STORY': return 'bg-blue-500 text-white'; case 'FEATURE': return 'bg-blue-600 text-white'; case 'TASK': return 'bg-green-500 text-white'; - case 'RESEARCH': return 'bg-blue-400 text-white'; - - // Quality & Issues case 'BUG': return 'bg-red-500 text-white'; - case 'ISSUE': return 'bg-red-400 text-white'; - case 'HOTFIX': return 'bg-red-600 text-white'; - - // Operations & Maintenance - case 'MAINTENANCE': return 'bg-orange-500 text-white'; - case 'DEPLOYMENT': return 'bg-orange-600 text-white'; - case 'MONITORING': return 'bg-orange-400 text-white'; - - // Documentation - case 'DOCUMENTATION': return 'bg-indigo-500 text-white'; - case 'SPECIFICATION': return 'bg-indigo-600 text-white'; - case 'GUIDE': return 'bg-indigo-400 text-white'; - - // Testing & Validation - case 'TEST': return 'bg-emerald-500 text-white'; - case 'REVIEW': return 'bg-emerald-600 text-white'; - case 'QA': return 'bg-emerald-400 text-white'; - - // Business & Sales - case 'LEAD': return 'bg-teal-500 text-white'; - case 'OPPORTUNITY': return 'bg-teal-600 text-white'; - case 'CONTRACT': return 'bg-teal-400 text-white'; - - // Creative & Design - case 'MOCKUP': return 'bg-pink-500 text-white'; - case 'PROTOTYPE': return 'bg-pink-600 text-white'; - case 'UI_DESIGN': return 'bg-pink-400 text-white'; - - // Support & Training - case 'SUPPORT': return 'bg-cyan-500 text-white'; - case 'TRAINING': return 'bg-cyan-600 text-white'; - - // Other - case 'NOTE': return 'bg-slate-500 text-white'; - case 'ACTION_ITEM': return 'bg-slate-600 text-white'; - case 'DECISION': return 'bg-slate-400 text-white'; + case 'MILESTONE': return 'bg-orange-500 text-black'; + case 'IDEA': return 'bg-yellow-500 text-white'; + case 'OUTCOME': return 'bg-indigo-500 text-white'; + case 'RESEARCH': return 'bg-teal-500 text-white'; default: return 'bg-gray-500 text-white'; } @@ -400,11 +364,11 @@ export function ListView() { const getStatusColor = (status: string) => { switch (status) { - case 'PROPOSED': return 'text-blue-400'; + case 'PROPOSED': return 'text-cyan-400'; case 'PLANNED': return 'text-purple-400'; case 'IN_PROGRESS': return 'text-yellow-400'; case 'COMPLETED': return 'text-green-400'; - case 'BLOCKED': return 'text-red-600'; + case 'BLOCKED': return 'text-red-400'; default: return 'text-gray-400'; } }; @@ -579,7 +543,7 @@ export function ListView() { {/* Status */}
- {node.status === 'PROPOSED' && } + {node.status === 'PROPOSED' && } {node.status === 'PLANNED' && } {node.status === 'IN_PROGRESS' && } {node.status === 'COMPLETED' && } @@ -607,12 +571,12 @@ export function ListView() { const statusConfig = { 'PROPOSED': { label: 'Proposed', - icon: , - color: 'bg-blue-500', + icon: , + color: 'bg-cyan-500', bgColor: 'bg-gray-750', - textColor: 'text-blue-400', + textColor: 'text-cyan-400', borderColor: 'border-gray-600', - dotColor: 'bg-blue-400' + dotColor: 'bg-cyan-400' }, 'PLANNED': { label: 'Planned', @@ -940,14 +904,14 @@ export function ListView() {
- {node.status === 'PROPOSED' && } + {node.status === 'PROPOSED' && } {node.status === 'PLANNED' && } {node.status === 'IN_PROGRESS' && } {node.status === 'COMPLETED' && } @@ -1363,14 +1327,14 @@ export function ListView() {
- {node.status === 'PROPOSED' && } + {node.status === 'PROPOSED' && } {node.status === 'PLANNED' && } {node.status === 'IN_PROGRESS' && } {node.status === 'COMPLETED' && } @@ -1570,12 +1534,12 @@ export function ListView() {
{/* Advanced Filters Row */} -
+