diff --git a/src/backend/controllers/pages.ts b/src/backend/controllers/pages.ts index da59766..ed5478c 100644 --- a/src/backend/controllers/pages.ts +++ b/src/backend/controllers/pages.ts @@ -1,5 +1,7 @@ import Page, { PageData } from '../models/page'; import Alias from '../models/alias'; +import PagesOrder from './pagesOrder'; +import PageOrder from '../models/pageOrder'; type PageDataFields = keyof PageData; @@ -63,59 +65,99 @@ class Pages { } /** - * Sort given pages with descending order of creation time + * Group all pages by their parents + * If the pageId is passed, it excludes passed page from result pages * - * @param {pages[]} pages - pages to sort - * @returns {page[]} - */ - public static sortByTimeDesc(pages: Page[]): Page[] { - return pages.sort((a, b) => { - if (a.body.time > b.body.time) { - return 1; - } - - if (a.body.time < b.body.time) { - return -1; - } - - return 0; - }); - } - - - /** - * Group given pages by their parents - * - * @param {pages[]} pages - pages to group + * @param {string} pageId - pageId to exclude from result pages * @returns {Page[]} */ - public static groupByParent(pages: Page[]): Page[] { + public static async groupByParent(pageId = ''): Promise { const result: Page[] = []; - const pagesGroupedByParent:Record> = {}; - - pages.forEach(page => { - pagesGroupedByParent[String(page._id)] = []; - - if (this.isRootPage(page)) { - pagesGroupedByParent[String(page._id)].push(page); - } else { - pagesGroupedByParent[String(page._parent)].push(page); - } - }); + const orderGroupedByParent: Record = {}; + const rootPageOrder = await PagesOrder.getRootPageOrder(); + const childPageOrder = await PagesOrder.getChildPageOrder(); + const orphanPageOrder: PageOrder[] = []; /** - * It converts grouped object to array - * Also removes redundant keys when there is no children + * If there is no root nad child page order, then it returns empty array */ - Object.entries(pagesGroupedByParent).forEach(([, value]) => { - if (value.length <= 0) { - return; - } else { - result.push(...value); - } - }); + if (!rootPageOrder || (!rootPageOrder && childPageOrder.length <= 0)) { + return []; + } - return result; + const pages = (await this.getAll()).reduce((map, _page) => { + map.set(_page._id, _page); + + return map; + }, new Map); + const rootPagesId = rootPageOrder.order; + + /** + * It groups root pages and 1 level pages by its parent + */ + rootPagesId.reduce((prev, curr, idx) => { + const childPages = childPageOrder.filter((child, _idx) => { + if (child.page === curr) { + childPageOrder.splice(_idx, 1); + + return child; + } + }); + const hasChildPage = childPages.length > 0; + + if (hasChildPage) { + prev[curr] = []; + prev[curr].push(curr); + prev[curr].push(...childPages[0].order); + } else { + prev[curr] = []; + prev[curr].push(curr); + } + + if (idx === rootPagesId.length - 1 && childPageOrder.length > 0) { + orphanPageOrder.push(...childPageOrder); + } + + return prev; + }, orderGroupedByParent); + + /** + * It groups remained ungrouped pages by its parent + */ + while (orphanPageOrder.length > 0) { + orphanPageOrder.forEach((orphanOrder, idx) => { + Object.entries(orderGroupedByParent).forEach(([key, value]) => { + if (orphanOrder.page && orphanOrder.order && value.includes(orphanOrder.page)) { + orderGroupedByParent[key].splice(value.indexOf(orphanOrder.page) + 1, 0, ...orphanOrder.order); + orphanPageOrder.splice(idx, 1); + } + }); + }); + } + + /** + * It converts grouped pages(object) to array + */ + Object.values(orderGroupedByParent).flatMap(arr => [ ...arr ]) + .forEach(arr => { + result.push(pages.get(arr)); + }); + + /** + * If the pageId passed, it excludes itself from result pages + * Otherwise just returns result itself + */ + if (pageId) { + return this.removeChildren(result, pageId).reduce((prev, curr) => { + if (curr instanceof Page) { + prev.push(curr); + } + + return prev; + }, Array()); + } else { + return result; + } } /** @@ -125,7 +167,7 @@ class Pages { * @param {string} parent - id of parent page * @returns {Array} */ - public static removeChildren(pagesAvailable: Array, parent: string | undefined): Array { + public static removeChildren(pagesAvailable: Array, parent: string | undefined): Array { pagesAvailable.forEach(async (item, index) => { if (item === null || item._parent !== parent) { return; @@ -259,16 +301,6 @@ class Pages { throw new Error('Please, fill page Header'); } } - - /** - * Check if the given page is root - * - * @param {page} page - page to check - * @returns {boolean} - */ - private static isRootPage(page:Page):boolean { - return page._parent === '0'; - } } export default Pages; diff --git a/src/backend/controllers/pagesOrder.ts b/src/backend/controllers/pagesOrder.ts index f084af3..846c9ac 100644 --- a/src/backend/controllers/pagesOrder.ts +++ b/src/backend/controllers/pagesOrder.ts @@ -33,6 +33,24 @@ class PagesOrder { return PageOrder.getAll(); } + /** + * Returns only root page's order + * + * @returns {Promise} + */ + public static async getRootPageOrder(): Promise { + return PageOrder.getRootPageOrder(); + } + + /** + * Returns only child page's order + * + * @returns {Promise} + */ + public static async getChildPageOrder(): Promise { + return PageOrder.getChildPageOrder(); + } + /** * Pushes the child page to the parent's order list * diff --git a/src/backend/models/pageOrder.ts b/src/backend/models/pageOrder.ts index 761fc6a..bf1df32 100644 --- a/src/backend/models/pageOrder.ts +++ b/src/backend/models/pageOrder.ts @@ -75,6 +75,28 @@ class PageOrder { return Promise.all(docs.map(doc => new PageOrder(doc))); } + /** + * Returns only root page's order + * + * @returns {Promise} + */ + public static async getRootPageOrder(): Promise { + const docs = await this.getAll(); + + return docs.filter(doc => doc.page === '0')[0]; + } + + /** + * Returns only child page's order + * + * @returns {Promise} + */ + public static async getChildPageOrder(): Promise { + const docs = await this.getAll(); + + return docs.filter(doc => doc.page !== '0'); + } + /** * constructor data setter * diff --git a/src/backend/routes/pages.ts b/src/backend/routes/pages.ts index 41869e5..fe70f37 100644 --- a/src/backend/routes/pages.ts +++ b/src/backend/routes/pages.ts @@ -11,9 +11,7 @@ const router = express.Router(); */ router.get('/page/new', verifyToken, allowEdit, async (req: Request, res: Response, next: NextFunction) => { try { - const pagesAvailable = await Pages.getAll(); - const pagesAvailableSorted = Pages.sortByTimeDesc(pagesAvailable); - const pagesAvailableGrouped = Pages.groupByParent(pagesAvailableSorted); + const pagesAvailableGrouped = await Pages.groupByParent(); res.render('pages/form', { pagesAvailableGrouped, @@ -34,6 +32,7 @@ router.get('/page/edit/:id', verifyToken, allowEdit, async (req: Request, res: R try { const page = await Pages.get(pageId); const pagesAvailable = await Pages.getAllExceptChildren(pageId); + const pagesAvailableGrouped = await Pages.groupByParent(pageId); if (!page._parent) { throw new Error('Parent not found'); @@ -44,7 +43,7 @@ router.get('/page/edit/:id', verifyToken, allowEdit, async (req: Request, res: R res.render('pages/form', { page, parentsChildrenOrdered, - pagesAvailable, + pagesAvailableGrouped, }); } catch (error) { res.status(404); diff --git a/src/backend/views/pages/form.twig b/src/backend/views/pages/form.twig index cb56940..91503e2 100644 --- a/src/backend/views/pages/form.twig +++ b/src/backend/views/pages/form.twig @@ -22,7 +22,7 @@ {% endif %}