Duffer Derek
import React, { createContext, useContext, useState, useEffect } from 'react';
import { headerConfig } from '../config/headerConfig';
import { footerConfig } from '../config/footerConfig';
import umbracoService from '../services/umbracoService';
import { mapUmbracoToHeaderProperties } from '../mappers/headerMapper';
import { mapUmbracoToFooterProperties } from '../mappers/footerMapper';
import { mapUmbracoToComponentData } from '../mappers/pageMapper';
import { v4 as uuidv4 } from 'uuid';
export interface DroppedItem {
id: string;
html: string;
uniqueId: string;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
properties: { [key: string]: any };
}
export interface Page {
id: string;
name: string;
url: string; // Add url property
isProtected: boolean;
documentType: {
id: string;
icon: string;
};
variants: {
state: string;
name: string;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
culture: any;
}[];
noAccess: boolean;
isTrashed: boolean;
parent: string;
hasChildren: boolean;
}
export const GRID_TYPES = {
DEFAULT: {
id: '99740655-b76b-4d34-aaac-aace495a39b6',
documentId: '99740655-b76b-4d34-aaac-aace495a39b6',
columns: 1
},
ONE_COLUMN: {
id: 'c996b4ec-1a0d-4a7c-b99d-466c2b153da4',
documentId: 'c996b4ec-1a0d-4a7c-b99d-466c2b153da4',
columns: 1
},
TWO_COLUMN: {
id: 'd6dbc5f5-510a-4020-be64-ef32b741be22',
documentId: 'd6dbc5f5-510a-4020-be64-ef32b741be22',
columns: 2
},
THREE_COLUMN: {
id: 'bfe13a0c-ab38-4f4f-8720-dcf34f8475d1',
documentId: 'bfe13a0c-ab38-4f4f-8720-dcf34f8475d1',
columns: 3
}
};
export interface GridLayout {
id: string;
documentId: string;
columns: (DroppedItem | GridLayout)[][];
parentId?: string;
}
interface GridState {
layouts: GridLayout[];
components: { [gridId: string]: DroppedItem[] };
}
interface CanvasContextProps {
droppedItems: { [pageId: string]: DroppedItem[] };
setDroppedItems: (pageId: string, items: DroppedItem[]) => void;
headerItem: DroppedItem;
setHeaderItem: React.Dispatch<React.SetStateAction<DroppedItem>>;
footerItem: DroppedItem;
setFooterItem: React.Dispatch<React.SetStateAction<DroppedItem>>;
pages: Page[];
setPages: React.Dispatch<React.SetStateAction<Page[]>>;
currentPage: Page | null;
setCurrentPage: React.Dispatch<React.SetStateAction<Page | null>>;
gridState: { [pageId: string]: GridState };
setGridState: (pageId: string, state: GridState) => void;
updateGridLayout: (pageId: string, gridId: string, updatedGrid: GridLayout) => void;
addGridLayout: (pageId: string, columns: number, parentId?: string) => void;
removeGridLayout: (pageId: string, gridId: string) => void;
addComponentToGrid: (pageId: string, gridId: string, columnIndex: number, component: DroppedItem) => void;
removeComponentFromGrid: (pageId: string, gridId: string, columnIndex: number, componentId: string) => void;
}
const CanvasContext = createContext<CanvasContextProps | undefined>(undefined);
export const CanvasProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
const [droppedItems, setDroppedItemsState] = useState<{ [pageId: string]: DroppedItem[] }>({});
const [headerItem, setHeaderItem] = useState<DroppedItem>({
id: 'header',
html: headerConfig.htmlTemplate(headerConfig.defaultProperties),
uniqueId: 'header',
properties: headerConfig.defaultProperties,
});
const [footerItem, setFooterItem] = useState<DroppedItem>({
id: 'footer',
html: footerConfig.htmlTemplate(footerConfig.defaultProperties),
uniqueId: 'footer',
properties: footerConfig.defaultProperties,
});
const [pages, setPages] = useState<Page[]>([]);
const [currentPage, setCurrentPage] = useState<Page | null>(null);
const [gridState, setGridStateInternal] = useState<{ [pageId: string]: GridState }>({});
// Add similar mapping functions for other components here...
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const getComponentData = async (umbracoData:any) => {
try {
return mapUmbracoToComponentData(umbracoData);
} catch (error) {
console.error('Failed to fetch component data:', error);
return {};
}
};
const fetchHeaderAndFooter = async () => {
try {
const commonNodeId = umbracoService.getCommonNodeId();
const commonNodeData = await umbracoService.fetchPageById(commonNodeId);
const headerProperties = mapUmbracoToHeaderProperties(commonNodeData);
const footerProperties = mapUmbracoToFooterProperties(commonNodeData);
setHeaderItem({
id: 'header',
html: headerConfig.htmlTemplate(headerProperties),
uniqueId: 'header',
properties: headerProperties,
});
setFooterItem({
id: 'footer',
html: footerConfig.htmlTemplate(footerProperties),
uniqueId: 'footer',
properties: footerProperties,
});
} catch (error) {
console.error('Failed to fetch header and footer data:', error);
}
};
useEffect(() => {
const fetchPages = async () => {
try {
const fetchedPages = await umbracoService.fetchPages();
setPages(fetchedPages);
if (fetchedPages && fetchedPages.length > 0) {
const firstPage = fetchedPages[0];
const currentPageData = await umbracoService.fetchPageById(firstPage.id);
debugger
if (currentPageData) {
const componentData = await getComponentData(currentPageData);
setCurrentPage({ ...firstPage, ...currentPageData });
if (componentData && firstPage.id) {
// Replace setDroppedItems with setGridState
setGridState(firstPage.id, componentData[firstPage.id] || { layouts: [], components: {} });
}
}
}
await fetchHeaderAndFooter();
} catch (error) {
console.error('Failed to fetch pages:', error);
}
};
fetchPages();
}, []);
const setDroppedItems = (pageId: string, items: DroppedItem[]) => {
if (!pageId) return;
setDroppedItemsState(prevState => ({
...prevState,
[pageId]: Array.isArray(items) ? items : []
}));
};
const setGridState = (pageId: string, state: GridState) => {
setGridStateInternal(prev => ({
...prev,
[pageId]: state
}));
};
const updateGridLayout = (pageId: string, gridId: string, updatedGrid: GridLayout) => {
setGridStateInternal(prev => {
const pageGrids = prev[pageId] || { layouts: [], components: {} };
const updateGridRecursively = (layouts: GridLayout[]): GridLayout[] => {
return layouts.map(grid => {
if (grid.id === gridId) {
return updatedGrid;
}
return {
...grid,
columns: grid.columns.map(column =>
column.map(item => {
if ('columns' in item) {
return {
...item,
columns: updateGridRecursively([item])[0].columns
};
}
return item;
})
)
};
});
};
return {
...prev,
[pageId]: {
...pageGrids,
layouts: updateGridRecursively(pageGrids.layouts)
}
};
});
};
const addGridLayout = (pageId: string, columns: number, parentId?: string) => {
let gridType;
switch (columns) {
case 1:
gridType = GRID_TYPES.ONE_COLUMN;
break;
case 2:
gridType = GRID_TYPES.TWO_COLUMN;
break;
case 3:
gridType = GRID_TYPES.THREE_COLUMN;
break;
default:
gridType = GRID_TYPES.DEFAULT;
}
const newGrid: GridLayout = {
id: `${gridType.id}-${uuidv4()}`, // Add unique identifier to the grid ID
documentId: gridType.id, // Keep the original content type key in documentId
columns: new Array(columns).fill(null).map(() => []),
parentId
};
setGridStateInternal(prev => {
const pageGrids = prev[pageId] || { layouts: [], components: {} };
if (!parentId) {
return {
...prev,
[pageId]: {
...pageGrids,
layouts: [...pageGrids.layouts, newGrid]
}
};
}
const updateParentGrid = (layouts: GridLayout[]): GridLayout[] => {
return layouts.map(grid => {
if (grid.id === parentId) {
return {
...grid,
columns: grid.columns.map(col => [...col, newGrid])
};
}
if (grid.columns) {
return {
...grid,
columns: grid.columns.map(col =>
col.map(item => {
if ('columns' in item) {
return updateParentGrid([item])[0];
}
return item;
})
)
};
}
return grid;
});
};
return {
...prev,
[pageId]: {
...pageGrids,
layouts: updateParentGrid(pageGrids.layouts)
}
};
});
};
const removeGridLayout = (pageId: string, gridId: string) => {
setGridStateInternal(prev => {
const pageGrids = prev[pageId] || { layouts: [], components: {} };
const removeGridRecursively = (layouts: GridLayout[]): GridLayout[] => {
return layouts
.filter(grid => grid.id !== gridId)
.map(grid => ({
...grid,
columns: grid.columns?.map(column =>
column?.filter(item => {
if (item && 'columns' in item) {
const updatedItem = removeGridRecursively([item as GridLayout])[0];
return updatedItem !== undefined;
}
return true;
})
) || []
}));
};
const newLayouts = removeGridRecursively(pageGrids.layouts);
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { [gridId]: removedComponents, ...remainingComponents } = pageGrids.components;
return {
...prev,
[pageId]: {
...pageGrids,
layouts: newLayouts,
components: remainingComponents
}
};
});
};
const addComponentToGrid = (
pageId: string,
gridId: string,
columnIndex: number,
component: DroppedItem
) => {
setGridStateInternal(prev => {
const pageGrids = prev[pageId] || { layouts: [], components: {} };
const updateGridRecursively = (layouts: GridLayout[]): GridLayout[] => {
return layouts.map(grid => {
if (grid.id === gridId) {
const newColumns = [...grid.columns];
newColumns[columnIndex] = [...(newColumns[columnIndex] || []), component];
return { ...grid, columns: newColumns };
}
return {
...grid,
columns: grid.columns.map(column =>
column.map(item => {
if ('columns' in item) {
return {
...item,
columns: updateGridRecursively([item])[0].columns
};
}
return item;
})
)
};
});
};
return {
...prev,
[pageId]: {
...pageGrids,
layouts: updateGridRecursively(pageGrids.layouts),
components: {
...pageGrids.components,
[gridId]: [...(pageGrids.components[gridId] || []), component]
}
}
};
});
};
const removeComponentFromGrid = (
pageId: string,
gridId: string,
columnIndex: number,
componentId: string
) => {
setGridStateInternal(prev => {
const pageGrids = prev[pageId] || { layouts: [], components: {} };
const updateGridRecursively = (layouts: GridLayout[]): GridLayout[] => {
return layouts.map(grid => {
if (grid.id === gridId) {
const newColumns = [...grid.columns];
newColumns[columnIndex] = newColumns[columnIndex].filter(
item => !('uniqueId' in item) || item.uniqueId !== componentId
);
return { ...grid, columns: newColumns };
}
return {
...grid,
columns: grid.columns.map(column =>
column.map(item => {
if ('columns' in item) {
return {
...item,
columns: updateGridRecursively([item])[0].columns
};
}
return item;
})
)
};
});
};
return {
...prev,
[pageId]: {
...pageGrids,
layouts: updateGridRecursively(pageGrids.layouts),
components: {
...pageGrids.components,
[gridId]: (pageGrids.components[gridId] || []).filter(
comp => comp.uniqueId !== componentId
)
}
}
};
});
};
// Initialize grid state for new pages
useEffect(() => {
const initializeGridState = () => {
if (pages.length > 0) {
pages.forEach(page => {
setGridState(page.id, {
layouts: [{
id: GRID_TYPES.DEFAULT.id,
documentId: GRID_TYPES.DEFAULT.documentId,
columns: [[]]
}],
components: {}
});
});
}
};
initializeGridState();
}, [pages]);
return (
<CanvasContext.Provider value={{ droppedItems, setDroppedItems, headerItem, setHeaderItem, footerItem, setFooterItem, pages, setPages, currentPage, setCurrentPage, gridState, setGridState, updateGridLayout, addGridLayout, removeGridLayout, addComponentToGrid, removeComponentFromGrid }}>
{children}
</CanvasContext.Provider>
);
};
export const useCanvasContext = () => {
const context = useContext(CanvasContext);
if (!context) {
throw new Error('useCanvasContext must be used within a CanvasProvider');
}
return context;
};
Sindbad File Manager Version 1.0, Coded By Sindbad EG ~ The Terrorists