diff --git a/django_project/frontend/src/pages/Dashboard/index.tsx b/django_project/frontend/src/pages/Dashboard/index.tsx index 09341e82..71a244da 100644 --- a/django_project/frontend/src/pages/Dashboard/index.tsx +++ b/django_project/frontend/src/pages/Dashboard/index.tsx @@ -54,7 +54,6 @@ const DashboardPage: React.FC = () => { const [chartsConfig, setChartsConfig] = useState([]); const [hasError, setHasError] = useState(false); const [isLayoutMenuOpen, setIsLayoutMenuOpen] = useState(false); - // pagination variables const [paginatedData, setPaginatedData] = useState([]); const [totalPages, setTotalPages] = useState(1); const [itemsPerPage, setItemsPerPage] = useState(6) @@ -63,8 +62,10 @@ const DashboardPage: React.FC = () => { const [filters, setFilters] = useState(null); const { dashboardUpdated } = useSelector((state: RootState) => state.dashboard); const [landscapes, setLandscapes] = useState([]); + const [layoutMode, setLayoutMode] = useState("horizontal"); // Default layout + const [layoutKey, setLayoutKey] = useState(0); + const [isMobile, setIsMobile] = useState(window.innerWidth < 768); const toast = useToast(); - const [panelPositions, setPanelPositions] = useState({}); const toggleMenu = () => { @@ -75,14 +76,19 @@ const DashboardPage: React.FC = () => { setIsLayoutMenuOpen(false); }; + const handleFilterClick = () => { + isOpen ? onClose() : onOpen(); + }; - - + const closeIsAnalysis = () => { + setIsAnalysisOpen(false) + }; useEffect(() => { dispatch(fetchDashboards(filters)); }, [dispatch, filters]); + useEffect(() => { if (dashboardUpdated) { dispatch(resetDashboardUpdated()); @@ -100,37 +106,20 @@ const DashboardPage: React.FC = () => { }, }); } - }, [dashboardUpdated]); - const handleFilterClick = () => { - if (isOpen) { - onClose(); - } else { - onOpen(); - } - }; - - const handleViewClick = (analysis: any) => { - setIsAnalysisOpen(true) - }; - - const closeIsAnakysis = () => { - setIsAnalysisOpen(false) - }; + // SEARCH FUNCTION const filteredData = chartsConfig.filter((chartConfig: any) => typeof chartConfig.title !== 'undefined' ? chartConfig.title.toLowerCase().includes(searchTerm.toLowerCase()) : false ); - + + // LAYOUT const rows = Math.ceil(filteredData.length / 3); - const [layoutMode, setLayoutMode] = useState("horizontal"); // Default layout - const [layoutKey, setLayoutKey] = useState(0); - const [isMobile, setIsMobile] = useState(window.innerWidth < 768); - // Effect to update layout mode when screen size changes + // RESPONSIVENESS useEffect(() => { const handleResize = () => { const mobileView = window.innerWidth < 768; @@ -148,14 +137,11 @@ const DashboardPage: React.FC = () => { return () => window.removeEventListener("resize", handleResize); }, []); + + // PAGINATION useEffect(() => { - // Recalculate items per page based on the layout mode const itemsPerPage = layoutMode === "horizontal" ? 6 : layoutMode === "vertical" ? 3 : 4; - - // Calculate total pages const totalPages = Math.ceil(filteredData.length / itemsPerPage); - - // Recalculate the paginated data for the current page const startIdx = (currentPage - 1) * itemsPerPage; const endIdx = startIdx + itemsPerPage; const paginatedData = filteredData.slice(startIdx, endIdx); @@ -173,419 +159,291 @@ const DashboardPage: React.FC = () => { } }; - var render = 0; const [dragPosition, setDragPosition] = useState({}); -const [cards, setCards] = useState<{ id: number; position: CardPosition }[]>([]); // Store card positions - -// Initialize cards with positions -const initialized = useRef(false); - -useEffect(() => { - if (!initialized.current && filteredData.length > 0) { - const initialCards = filteredData.map((_, index) => ({ - id: index, - position: { row: Math.floor(index / 3), col: index % 3 }, - })); - setCards(initialCards); - initialized.current = true; // Prevents re-initialization - } -}, [filteredData]); - -// Handler for dragging move (during dragging) -const handleDragMove = (data: DraggableData, panelKey: number) => { - setDragPosition((prevState) => ({ - ...prevState, - [panelKey]: { x: data.x, y: data.y }, - })); -}; - -const handleDragStop = (panelKey: number, data: any) => { - const newX = data.x; - const newY = data.y; - - // Logic to check the new position and swap cards - const currentCard = cards.find((card) => card.id === panelKey); - if (!currentCard) return; - - // Calculate the target row and column based on the drag position - let targetRow = Math.floor(newY / 200); // Assuming each card height is 200px - let targetCol = Math.floor(newX / 200); // Assuming each card width is 200px + const [cards, setCards] = useState<{ id: number; position: CardPosition }[]>([]); // Store card positions - // Ensure we stay within bounds of the grid - const maxRows = Math.ceil(cards.length / 3); // assuming 3 cards per row - const maxCols = 3; // 3 columns per row - - if (targetRow >= maxRows) targetRow = maxRows - 1; - if (targetCol >= maxCols) targetCol = maxCols - 1; - - // Find the card at the target position - const targetCard = cards.find( - (card) => card.position.row === targetRow && card.position.col === targetCol - ); + // Initialize cards with positions + const initialized = useRef(false); - if (targetCard && currentCard.id !== targetCard.id) { - // Swap positions of the cards - setCards((prevCards) => { - const updatedCards = [...prevCards]; - const currentIndex = updatedCards.findIndex((card) => card.id === panelKey); - const targetIndex = updatedCards.findIndex((card) => card.id === targetCard.id); - - if (currentIndex >= 0 && targetIndex >= 0) { - // Swap the positions of the two cards - updatedCards[currentIndex].position = targetCard.position; - updatedCards[targetIndex].position = currentCard.position; + useEffect(() => { + if (!initialized.current && filteredData.length > 0) { + const initialCards = filteredData.map((_, index) => ({ + id: index, + position: { row: Math.floor(index / 3), col: index % 3 }, + })); + setCards(initialCards); + initialized.current = true; // Prevents re-initialization + } + }, [filteredData]); + + + + + const moveCard = (cardId: any, direction: string) => { + setChartsConfig((prevConfig) => { + const index = prevConfig.findIndex((chart) => chart.card.id === cardId); + if (index === -1) return prevConfig; + + const rowSize = 3; // Assuming 3 columns per row + let newIndex = index; + + switch (direction) { + case "left": + if (index % rowSize !== 0) newIndex = index - 1; + break; + case "right": + if (index % rowSize !== rowSize - 1 && index + 1 < prevConfig.length) + newIndex = index + 1; + break; + case "up": + if (index - rowSize >= 0) newIndex = index - rowSize; + break; + case "down": + if (index + rowSize < prevConfig.length) newIndex = index + rowSize; + break; + default: + return prevConfig; } - return updatedCards; + if (newIndex !== index) { + const updatedConfig = [...prevConfig]; + [updatedConfig[index], updatedConfig[newIndex]] = [updatedConfig[newIndex], updatedConfig[index]]; + return updatedConfig.map((chart, i) => ({ + ...chart, + card: { + ...chart.card, + position: { row: Math.floor(i / rowSize), col: i % rowSize }, + }, + })); + } + return prevConfig; }); - } - - // Reset the drag position after moving - setDragPosition((prevState) => ({ - ...prevState, - [panelKey]: { x: 0, y: 0 }, - })); -}; - -const moveCard = (panelKey: number, direction: string) => { - setCards((prevCards) => { - const updatedCards = [...prevCards]; - const currentIndex = updatedCards.findIndex((card) => card.id === panelKey); - - if (currentIndex === -1) return prevCards; - - const currentCard = updatedCards[currentIndex]; - let targetRow = currentCard.position.row; - let targetCol = currentCard.position.col; - - // Determine new position based on direction - if (direction === "left") targetCol -= 1; - if (direction === "right") targetCol += 1; - if (direction === "up") targetRow -= 1; - if (direction === "down") targetRow += 1; - - // Find target card - const targetIndex = updatedCards.findIndex( - (card) => card.position.row === targetRow && card.position.col === targetCol - ); - - if (targetIndex !== -1) { - // Clone cards to ensure state updates correctly - const newCards = updatedCards.map((card, index) => - index === currentIndex - ? { ...card, position: { row: targetRow, col: targetCol } } // Swap current card - : index === targetIndex - ? { ...card, position: { row: currentCard.position.row, col: currentCard.position.col } } // Swap target card - : card - ); - - return newCards; // ✅ Return a new array reference to trigger a re-render - } - - return prevCards; // No change if target position is invalid - }); -}; + }; + // INTIALIZE CARDS + useEffect(() => { + if (!loading && Array.isArray(dashboardData)) { + + const updatedChartsConfig = dashboardData.map((dashboard, index) => { + + const matchingCard = cards.find((card) => card.id === index + 1) || { + id: index + 1, + position: { row: Math.floor(index / 3), col: index % 3 }, + }; + + return { + config: dashboard.config, + analysisResults: dashboard.analysis_results, + title: dashboard.title, + uuid: dashboard.uuid, + owner: dashboard.owner, + privacy_type: dashboard.privacy_type, + card: matchingCard + } + }); -useEffect(() => { - if (!loading && Array.isArray(dashboardData)) { - - - - const updatedChartsConfig = dashboardData.map((dashboard, index) => { + // Set the state to pass down to the chart cards + setChartsConfig(updatedChartsConfig); - const matchingCard = cards.find((card) => card.id === index + 1) || { - id: index + 1, - position: { row: Math.floor(index / 3), col: index % 3 }, - }; - - return { - config: dashboard.config, - analysisResults: dashboard.analysis_results, - title: dashboard.title, - uuid: dashboard.uuid, - owner: dashboard.owner, - privacy_type: dashboard.privacy_type, - card: matchingCard + if (dashboardData && Array.isArray(dashboardData)) { + const extractedLandscapes = dashboardData.flatMap((data) => { + if (data.analysis_results) { + return data.analysis_results.flatMap((result: any) => { + if (result.analysis_results) { + return [result.analysis_results.data.landscape]; + } + return []; + }); + } + return []; + }); + + // Remove duplicates + const uniqueLandscapes = Array.from(new Set(extractedLandscapes)); + setLandscapes(uniqueLandscapes); } - }); - - // Set the state to pass down to the chart cards - setChartsConfig(updatedChartsConfig); - - if (dashboardData && Array.isArray(dashboardData)) { - const extractedLandscapes = dashboardData.flatMap((data) => { - if (data.analysis_results) { - return data.analysis_results.flatMap((result: any) => { - if (result.analysis_results) { - return [result.analysis_results.data.landscape]; - } - return []; - }); - } - return []; - }); - - // Remove duplicates - const uniqueLandscapes = Array.from(new Set(extractedLandscapes)); - setLandscapes(uniqueLandscapes); + + } - - - } -}, [loading, dashboardData, cards]); + }, [loading, dashboardData, cards]); - const renderPanels = () => { - try { - const paginatedData = filteredData.slice(startIdx, endIdx); +const renderPanels = () => { + try { + const paginatedData = filteredData.slice(startIdx, endIdx); - return ( - - {Array.from({ length: rows }).map((_, rowIndex) => { - const rowPanels = paginatedData.slice(rowIndex * 3, rowIndex * 3 + 3); + return ( + + {layoutMode === "horizontal" && (() => { + const remainder = chartsConfig.length % 3; + const extraPanels = remainder === 0 ? 0 : 3 - remainder; + const balancedPanels = [...chartsConfig, ...Array(extraPanels).fill(null)]; + + const sortedCharts = balancedPanels + .filter(Boolean) + .sort((a, b) => + a.card.position.row === b.card.position.row + ? a.card.position.col - b.card.position.col + : a.card.position.row - b.card.position.row + ); + + return ( + + {Array.from({ length: rows }).map((_, rowIndex) => { + const rowPanels = sortedCharts.slice(rowIndex * 3, rowIndex * 3 + 3); + return ( + + {rowPanels.map((chart) => ( + + +
+ +
- if (layoutMode === "horizontal") { - render = 0; - const remainder = chartsConfig.length % 3; - const extraPanels = remainder === 0 ? 0 : 3 - remainder; - - // Create an array with original charts + dummy panels to maintain grid layout - const balancedPanels = [...chartsConfig, ...Array(extraPanels).fill(null)]; - - console.log("Current Card Positions:", chartsConfig); - - // Sort charts based on card positions - const sortedCharts = [...balancedPanels].filter(Boolean).sort((a, b) => - a.card.position.row === b.card.position.row - ? a.card.position.col - b.card.position.col - : a.card.position.row - b.card.position.row - ); - - return ( - - {Array.from({ length: rows }).map((_, rowIndex) => { - const rowPanels = sortedCharts.slice(rowIndex * 3, rowIndex * 3 + 3); - - return ( - - {rowPanels.map((chart, index) => ( - - -
- - {chart.card.id} -
- - {/* Arrows for moving the card */} - - - - - - -
-
- ))} -
- ); - })} -
- ); + + + + + + +
+
+ ))} +
+ ); + })} +
+ ); + })()} + + {layoutMode === "vertical" && (() => { + const extraPanels = paginatedData.length % 3 === 0 ? 0 : 3 - (paginatedData.length % 3); + const balancedPanels = [...paginatedData, ...Array(extraPanels).fill(null)]; + + return ( + + {balancedPanels.map((config, index) => { + const isDummy = config === null; + return ( + + + + + {!isDummy && } + + + + {!isDummy && index !== balancedPanels.length - 1 && } + + ); + })} + + ); + })()} + + {layoutMode === "nested" && (() => { + const smallPanels: any[] = []; + const mainPanels: any[] = []; + + paginatedData.forEach((config, index) => { + if (index % 4 === 0) { + mainPanels.push(config); + } else { + smallPanels.push(config); } - - + }); - if (layoutMode === "vertical") { - render = 0; - const remainder = rowPanels.length % 3; - const extraPanels = remainder === 0 ? 0 : 3 - remainder; - const balancedPanels = [...rowPanels, ...Array(extraPanels).fill(null)]; - if (rowPanels.length === 0) { - return null; - } - - return ( - - {balancedPanels.map((config, index) => { - const panelKey = rowIndex * 3 + index; - const isDummy = config === null; - - return ( - - + + + {smallPanels.map((config, index) => ( + + + - - - {!isDummy && -
- -
- } -
-
-
- - {!isDummy && index !== balancedPanels.length - 1 && ( - - )} -
- ); - })} + + +
+ {index !== smallPanels.length - 1 && } +
+ ))}
- ); - } - - + + + + + + + {mainPanels.length > 0 ? ( + mainPanels.map((config) => ( + + )) + ) : ( + No main panels to display + )} + + + + ); + })()} + + {/* Pagination controls */} + +
+ ); + } catch (error) { + console.error("Error rendering panels:", error); + setHasError(true); + } +}; - if (layoutMode === "nested" && render == 0) { - ++render; - const totalPanels = filteredData.length; - const smallPanels: any[] = []; - const mainPanels: any[] = []; - - // Distribute panels dynamically - filteredData.forEach((config, index) => { - if (index % 4 === 0) { - mainPanels.push(config); // Every 4th panel goes to Main Panel - } else { - smallPanels.push(config); // Others go to Small Panels - } - }); - - console.log('main panels', mainPanels); - - return ( - - {/* Small Panels Container */} - - - {smallPanels.map((config, index) => ( - - - -
- -
-
-
- {index !== smallPanels.length - 1 && } -
- ))} -
-
- - - - {/* Main Panel Container */} - - - {mainPanels.length > 0 ? ( - mainPanels.map((config) => ( -
- -
- )) - ) : ( - No main panels to display - )} -
-
-
- ); - } - - return null; - })} - - {/* Pagination controls */} - -
- ); - } catch (error) { - console.error("Error rendering panels:", error); - setHasError(true); - } - }; @@ -781,7 +639,7 @@ useEffect(() => { /> {/* Analysis sidebar */} - { closeIsAnakysis(); } } selectedAnalysis={null} /> + { closeIsAnalysis(); } } selectedAnalysis={null} />