1
0
Fork 0
mirror of https://github.com/codex-team/codex.docs.git synced 2025-07-19 05:09:41 +02:00

Fix sorting in dropdown (#187)

* fix: fix order of page categories with raw type

* refactor: change method location and others

* refactor: fix method names and variables also split methods to be clear their role

* fix: fix variable name

* fix: change the method to group of pages

* refactor: replace filter metethod to querying database

* refactor: fix typo, rename variable, add comments, improve code quality

* fix: add exit infinite loop

* fix: replace exiting loop to throwing exception
This commit is contained in:
YeoKyung Yoon 2022-06-22 07:09:08 -07:00 committed by GitHub
parent 30d96909d3
commit b3d8a1bfd4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 191 additions and 5 deletions

View file

@ -1,5 +1,8 @@
import Page, { PageData } from '../models/page';
import Alias from '../models/alias';
import PagesOrder from './pagesOrder';
import PageOrder from '../models/pageOrder';
import HttpException from "../exceptions/httpException";
type PageDataFields = keyof PageData;
@ -62,6 +65,121 @@ class Pages {
return nullFilteredPages;
}
/**
* Group all pages by their parents
* If the pageId is passed, it excludes passed page from result pages
*
* @param {string} pageId - pageId to exclude from result pages
* @returns {Page[]}
*/
public static async groupByParent(pageId = ''): Promise<Page[]> {
const result: Page[] = [];
const orderGroupedByParent: Record<string, string[]> = {};
const rootPageOrder = await PagesOrder.getRootPageOrder();
const childPageOrder = await PagesOrder.getChildPageOrder();
const orphanPageOrder: PageOrder[] = [];
/**
* If there is no root and child page order, then it returns an empty array
*/
if (!rootPageOrder || (!rootPageOrder && childPageOrder.length <= 0)) {
return [];
}
const pages = (await this.getAll()).reduce((map, _page) => {
map.set(_page._id, _page);
return map;
}, new Map);
const idsOfRootPages = rootPageOrder.order;
/**
* It groups root pages and 1 level pages by its parent
*/
idsOfRootPages.reduce((prev, curr, idx) => {
const childPages:PageOrder[] = [];
childPageOrder.forEach((pageOrder, _idx) => {
if (pageOrder.page === curr) {
childPages.push(pageOrder);
childPageOrder.splice(_idx, 1);
}
});
const hasChildPage = childPages.length > 0;
prev[curr] = [];
prev[curr].push(curr);
/**
* It attaches 1 level page id to its parent page id
*/
if (hasChildPage) {
prev[curr].push(...childPages[0].order);
}
/**
* If non-attached childPages which is not 1 level page still remains,
* It is stored as an orphan page so that it can be processed in the next statements
*/
if (idx === idsOfRootPages.length - 1 && childPageOrder.length > 0) {
orphanPageOrder.push(...childPageOrder);
}
return prev;
}, orderGroupedByParent);
let count = 0;
/**
* It groups remained ungrouped pages by its parent
*/
while (orphanPageOrder.length > 0) {
if (count >= 1000) {
throw new HttpException(500, `Page cannot be processed`);
}
orphanPageOrder.forEach((orphanOrder, idx) => {
// It loops each of grouped orders formatted as [root page id(1): corresponding child pages id(2)]
Object.entries(orderGroupedByParent).forEach(([parentPageId, value]) => {
// If (2) contains orphanOrder's parent id(page)
if (orphanOrder.page && orphanOrder.order && value.includes(orphanOrder.page)) {
// Append orphanOrder's id(order) into its parent id
orderGroupedByParent[parentPageId].splice(value.indexOf(orphanOrder.page) + 1, 0, ...orphanOrder.order);
// Finally, remove orphanOrder from orphanPageOrder
orphanPageOrder.splice(idx, 1);
}
});
});
count += 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<Page>());
} else {
return result;
}
}
/**
* Set all children elements to null
*

View file

@ -33,6 +33,24 @@ class PagesOrder {
return PageOrder.getAll();
}
/**
* Returns only root page's order
*
* @returns {Promise<PageOrder[]>}
*/
public static async getRootPageOrder(): Promise<PageOrder> {
return PageOrder.getRootPageOrder();
}
/**
* Returns only child page's order
*
* @returns {Promise<PageOrder[]>}
*/
public static async getChildPageOrder(): Promise<PageOrder[]> {
return PageOrder.getChildPageOrder();
}
/**
* Pushes the child page to the parent's order list
*

View file

@ -75,6 +75,28 @@ class PageOrder {
return Promise.all(docs.map(doc => new PageOrder(doc)));
}
/**
* Returns only root page's order
*
* @returns {Promise<PageOrder[]>}
*/
public static async getRootPageOrder(): Promise<PageOrder> {
const docs = await db.findOne({ 'page': '0' });
return new PageOrder(docs);
}
/**
* Returns only child page's order
*
* @returns {Promise<PageOrder[]>}
*/
public static async getChildPageOrder(): Promise<PageOrder[]> {
const docs = await this.getAll({ 'page': { $ne: '0' } });
return Promise.all(docs.map(doc => new PageOrder(doc)));
}
/**
* constructor data setter
*

View file

@ -11,10 +11,10 @@ const router = express.Router();
*/
router.get('/page/new', verifyToken, allowEdit, async (req: Request, res: Response, next: NextFunction) => {
try {
const pagesAvailable = await Pages.getAll();
const pagesAvailableGrouped = await Pages.groupByParent();
res.render('pages/form', {
pagesAvailable,
pagesAvailableGrouped,
page: null,
});
} catch (error) {
@ -32,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');
@ -42,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);

View file

@ -22,7 +22,7 @@
{% endif %}
<select name="parent">
<option value="0">Root</option>
{% for _page in pagesAvailable %}
{% for _page in pagesAvailableGrouped %}
{% if _page._id != currentPageId %}
<option value="{{ _page._id }}" {{ page is not empty and page._parent == _page._id ? 'selected' : ''}}>
{% if _page._parent != "0" %}

View file

@ -146,4 +146,31 @@ describe('PageOrder model', () => {
await pageOrder.destroy();
});
it('Testing get parents and children order methods', async () => {
const parentTestData = {
page: '0',
order: ['1', '2', '3', '4', '5'],
};
const childTestData = {
page: 'child',
order: ['a', 'b', 'c', 'd', 'e'],
};
const parentOrder = new PageOrder(parentTestData);
const childOrder = new PageOrder(childTestData);
const insertedParentOrder = await parentOrder.save();
const insertedChildOrder = await childOrder.save();
const fetchedParentOrder = await PageOrder.getRootPageOrder();
const fetchedChildOrder = await PageOrder.getChildPageOrder();
expect(fetchedParentOrder.page).to.deep.equals(parentTestData.page);
expect(fetchedParentOrder.order).to.deep.equal(parentTestData.order);
expect(fetchedChildOrder).to.be.an('array').that.is.length(1);
expect(fetchedChildOrder[0].page).to.deep.equals(childTestData.page);
expect(fetchedChildOrder[0].order).to.deep.equals(childTestData.order);
await insertedParentOrder.destroy();
await insertedChildOrder.destroy();
});
});