import type { RecursivePartial, RecursiveRequired } from '../api/utils/types' import filterEdges from '../api/utils/filter-edges' import { VendureConfig, getConfig } from '../api' export const getCollectionsQuery = /* GraphQL */ ` query getCollections { collections { items { id name description slug productVariants { totalItems } parent { id } children { id } } } } `; async function getSiteInfo({ query = getCollectionsQuery, variables, config, }: { query?: string variables?: any config?: VendureConfig preview?: boolean } = {}): Promise { config = getConfig(config) // RecursivePartial forces the method to check for every prop in the data, which is // required in case there's a custom `query` const { data } = await config.fetch( query, { variables } ) const categories = arrayToTree(data.collections?.items.map(i => ({ ...i, entityId: i.id, name: i.name, path: i.slug, description: i.description, productCount: i.productVariants.totalItems, }))).children; const brands = data.site?.brands?.edges return { categories: (categories as RecursiveRequired) ?? [], brands: [], } } export default getSiteInfo export type HasParent = { id: string; parent?: { id: string } | null }; export type TreeNode = T & { children: Array>; expanded: boolean }; export type RootNode = { id?: string; children: Array> }; export function arrayToTree(nodes: T[], currentState?: RootNode): RootNode { const topLevelNodes: Array> = []; const mappedArr: { [id: string]: TreeNode } = {}; const currentStateMap = treeToMap(currentState); // First map the nodes of the array to an object -> create a hash table. for (const node of nodes) { mappedArr[node.id] = { ...(node as any), children: [] }; } for (const id of nodes.map(n => n.id)) { if (mappedArr.hasOwnProperty(id)) { const mappedElem = mappedArr[id]; mappedElem.expanded = currentStateMap.get(id)?.expanded ?? false; const parent = mappedElem.parent; if (!parent) { continue; } // If the element is not at the root level, add it to its parent array of children. const parentIsRoot = !mappedArr[parent.id]; if (!parentIsRoot) { if (mappedArr[parent.id]) { mappedArr[parent.id].children.push(mappedElem); } else { mappedArr[parent.id] = { children: [mappedElem] } as any; } } else { topLevelNodes.push(mappedElem); } } } // tslint:disable-next-line:no-non-null-assertion const rootId = topLevelNodes.length ? topLevelNodes[0].parent!.id : undefined; return { id: rootId, children: topLevelNodes }; } /** * Converts an existing tree (as generated by the arrayToTree function) into a flat * Map. This is used to persist certain states (e.g. `expanded`) when re-building the * tree. */ function treeToMap(tree?: RootNode): Map> { const nodeMap = new Map>(); function visit(node: TreeNode) { nodeMap.set(node.id, node); node.children.forEach(visit); } if (tree) { visit(tree as TreeNode); } return nodeMap; }