From 2bf2b8904b405468bf5cdea571dfc16b2e534e58 Mon Sep 17 00:00:00 2001 From: Sohee Lim Date: Tue, 5 Nov 2024 17:19:51 -0500 Subject: [PATCH] feat: add side nav api --- assets/shared-bundle.js | 37 ++++--- assets/tailwind-output.css | 8 +- rollup.config.mjs | 1 + src/lib/types.ts | 20 ++++ src/modules/side-nav/SideNavModule.tsx | 11 ++ src/modules/side-nav/api.ts | 140 +++++++++++++++++++++++++ src/modules/side-nav/index.ts | 2 + src/modules/side-nav/renderSideNav.tsx | 18 ++++ templates/document_head.hbs | 1 + templates/home_page.hbs | 19 +++- 10 files changed, 237 insertions(+), 20 deletions(-) create mode 100644 src/modules/side-nav/SideNavModule.tsx create mode 100644 src/modules/side-nav/api.ts create mode 100644 src/modules/side-nav/index.ts create mode 100644 src/modules/side-nav/renderSideNav.tsx diff --git a/assets/shared-bundle.js b/assets/shared-bundle.js index 079ceab78..7a0ff7f4d 100644 --- a/assets/shared-bundle.js +++ b/assets/shared-bundle.js @@ -19850,20 +19850,29 @@ var getIntrinsic = function GetIntrinsic(name, allowMissing) { var callBind$3 = {exports: {}}; -var GetIntrinsic$3 = getIntrinsic; +var esDefineProperty; +var hasRequiredEsDefineProperty; -/** @type {import('.')} */ -var $defineProperty$2 = GetIntrinsic$3('%Object.defineProperty%', true) || false; -if ($defineProperty$2) { - try { - $defineProperty$2({}, 'a', { value: 1 }); - } catch (e) { - // IE 8 has a broken defineProperty - $defineProperty$2 = false; +function requireEsDefineProperty () { + if (hasRequiredEsDefineProperty) return esDefineProperty; + hasRequiredEsDefineProperty = 1; + + var GetIntrinsic = getIntrinsic; + + /** @type {import('.')} */ + var $defineProperty = GetIntrinsic('%Object.defineProperty%', true) || false; + if ($defineProperty) { + try { + $defineProperty({}, 'a', { value: 1 }); + } catch (e) { + // IE 8 has a broken defineProperty + $defineProperty = false; + } } -} -var esDefineProperty = $defineProperty$2; + esDefineProperty = $defineProperty; + return esDefineProperty; +} var GetIntrinsic$2 = getIntrinsic; @@ -19880,7 +19889,7 @@ if ($gOPD$1) { var gopd$1 = $gOPD$1; -var $defineProperty$1 = esDefineProperty; +var $defineProperty$1 = requireEsDefineProperty(); var $SyntaxError = syntax; var $TypeError$3 = type; @@ -19935,7 +19944,7 @@ var defineDataProperty$1 = function defineDataProperty( } }; -var $defineProperty = esDefineProperty; +var $defineProperty = requireEsDefineProperty(); var hasPropertyDescriptors = function hasPropertyDescriptors() { return !!$defineProperty; @@ -20010,7 +20019,7 @@ callBind$3.exports; var $call = GetIntrinsic('%Function.prototype.call%'); var $reflectApply = GetIntrinsic('%Reflect.apply%', true) || bind.call($call, $apply); - var $defineProperty = esDefineProperty; + var $defineProperty = requireEsDefineProperty(); var $max = GetIntrinsic('%Math.max%'); module.exports = function callBind(originalFunction) { diff --git a/assets/tailwind-output.css b/assets/tailwind-output.css index c3ed274df..1988ab484 100644 --- a/assets/tailwind-output.css +++ b/assets/tailwind-output.css @@ -934,14 +934,14 @@ video { height: 2rem; } -.h-padding-large { - height: 1.5rem; -} - .h-full { height: 100%; } +.h-padding-large { + height: 1.5rem; +} + .min-h-4 { min-height: 1rem; } diff --git a/rollup.config.mjs b/rollup.config.mjs index 6615a153e..6b6af3c2f 100644 --- a/rollup.config.mjs +++ b/rollup.config.mjs @@ -41,6 +41,7 @@ export default defineConfig([ 'article-page': 'src/modules/article-page/index.ts', 'category-page': 'src/modules/category-page/index.ts', 'section-page': 'src/modules/section-page/index.ts', + 'side-nav': 'src/modules/side-nav/index.ts', }, output: { dir: 'assets', diff --git a/src/lib/types.ts b/src/lib/types.ts index 94e54b739..a31859b77 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -226,3 +226,23 @@ export type CategoryPageData = { export type SectionPageData = { section: Section; }; + +export type SideNavData = { + categories: { + id: number; + url: string; + name: string; + position: number; + sections: { + name: string; + id: number; + position: number; + url: string; + articles: { + name: string; + id: number; + url: string; + }[]; + }[]; + }[]; +}; diff --git a/src/modules/side-nav/SideNavModule.tsx b/src/modules/side-nav/SideNavModule.tsx new file mode 100644 index 000000000..f5cedaee3 --- /dev/null +++ b/src/modules/side-nav/SideNavModule.tsx @@ -0,0 +1,11 @@ +import { FC } from 'react'; +import { SideNavData } from '../../lib/types'; +import cn from 'classnames'; + +type Props = { + sideNavData: SideNavData; +}; + +export const SideNav: FC = ({ sideNavData }) => { + return
Siiiide naaav
; +}; diff --git a/src/modules/side-nav/api.ts b/src/modules/side-nav/api.ts new file mode 100644 index 000000000..3fe24dc98 --- /dev/null +++ b/src/modules/side-nav/api.ts @@ -0,0 +1,140 @@ +import { SideNavData } from '../../lib/types'; + +const makeArrayToHaveUniqueValues = (array: { id: number }[]) => { + return array.filter((value, index, self) => self.findIndex((v) => v.id === value.id) === index); +}; + +type SideNavApiResponse = { + articles: { + section_id: number; + id: number; + url: string; + name: string; + position: number; + }[]; + categories: { + id: number; + position: number; + url: string; + name: string; + }[]; + sections: { + category_id: number; + id: number; + url: string; + name: string; + position: number; + }[]; +}; + +const sanitizeResponse = (response: SideNavApiResponse): SideNavData => { + if (!response) { + return null; + } + + if (!response.articles || !response.categories || !response.sections) { + return null; + } + + const categories = response.categories.map((category) => { + const sections = response.sections + .filter((section) => section.category_id === category.id) + .map((section) => { + const articles = response.articles + .filter((article) => article.section_id === section.id) + .map((article) => ({ + id: article.id, + name: article.name, + url: article.url, + position: article.position, + })); + + return { + id: section.id, + name: section.name, + position: section.position, + url: section.url, + articles: articles.sort((a, b) => a.position - b.position), + }; + }); + + return { + id: category.id, + name: category.name, + position: category.position, + url: category.url, + sections: sections.sort((a, b) => a.position - b.position), + }; + }); + + return { categories: categories.sort((a, b) => a.position - b.position) }; +}; + +export const sideNav = { + get: async (): Promise => { + const url = `${window.location.origin}/api/v2/help_center/en-us/articles.json?include=categories,sections&per_page=100`; + + try { + const response = await fetch(url); + if (!response.ok) { + throw new Error(`Response status: ${response.status}`); + } + + const responseData = await response.json(); + + if (responseData.page_count <= 1) { + return sanitizeResponse(responseData); + } + + // fetch the rest of the pages since first page is already fetched above. + const pages = await Promise.all( + Array.from({ length: responseData.page_count - 1 }, (_, i) => { + return sideNav.getPage(i + 2); + }) + ); + + const allPagesArticleResponseData = makeArrayToHaveUniqueValues( + responseData.articles.concat( + ...pages.map((page: SideNavApiResponse | null) => page?.articles || []) + ) + ); + const allPagesSectionResponseData = makeArrayToHaveUniqueValues( + responseData.sections.concat( + ...pages.map((page: SideNavApiResponse | null) => page?.sections || []) + ) + ); + const allPagesCategoryResponseData = makeArrayToHaveUniqueValues( + responseData.categories.concat( + ...pages.map((page: SideNavApiResponse | null) => page?.categories || []) + ) + ); + const allPagesResponseData = { + ...responseData, + articles: allPagesArticleResponseData, + sections: allPagesSectionResponseData, + categories: allPagesCategoryResponseData, + }; + const sanitizedResponse = sanitizeResponse(allPagesResponseData); + console.log(sanitizedResponse); + return sanitizedResponse; + } catch (error) { + console.error(error); + return null; + } + }, + getPage: async (page: number): Promise => { + const url = `${window.location.origin}/api/v2/help_center/en-us/articles.json?include=categories,sections&page=${page}&per_page=100`; + + try { + const response = await fetch(url); + if (!response.ok) { + throw new Error(`Response status: ${response.status}`); + } + + return await response.json(); + } catch (error) { + console.error(error); + return null; + } + }, +}; diff --git a/src/modules/side-nav/index.ts b/src/modules/side-nav/index.ts new file mode 100644 index 000000000..6e40a1c7b --- /dev/null +++ b/src/modules/side-nav/index.ts @@ -0,0 +1,2 @@ +export { renderSideNav } from './renderSideNav'; +export { sideNav } from './api'; diff --git a/src/modules/side-nav/renderSideNav.tsx b/src/modules/side-nav/renderSideNav.tsx new file mode 100644 index 000000000..dd98d2ede --- /dev/null +++ b/src/modules/side-nav/renderSideNav.tsx @@ -0,0 +1,18 @@ +import { render } from 'react-dom'; +import { SideNavData } from '../../lib/types'; +import { Settings } from '../shared'; +import { createTheme, ThemeProviders } from '../shared'; +import { SideNav } from './SideNavModule'; + +export async function renderSideNav( + settings: Settings, + sideNavData: SideNavData, + container: HTMLElement +) { + render( + + + , + container + ); +} diff --git a/templates/document_head.hbs b/templates/document_head.hbs index 3a3a99a7b..7489a80a0 100644 --- a/templates/document_head.hbs +++ b/templates/document_head.hbs @@ -19,6 +19,7 @@ "article-page": "{{asset 'article-page-bundle.js'}}", "category-page": "{{asset 'category-page-bundle.js'}}", "section-page": "{{asset 'section-page-bundle.js'}}", + "side-nav": "{{asset 'side-nav-bundle.js'}}", "new-request-form-translations": "{{asset 'new-request-form-translations-bundle.js'}}", "shared": "{{asset 'shared-bundle.js'}}", "wysiwyg": "{{asset 'wysiwyg-bundle.js'}}", diff --git a/templates/home_page.hbs b/templates/home_page.hbs index b1f5926bc..351dd5093 100644 --- a/templates/home_page.hbs +++ b/templates/home_page.hbs @@ -1,5 +1,5 @@
- +
@@ -9,7 +9,6 @@ const container = document.getElementById("new-homepage"); const settings = {{json settings}}; - // Handles data for the homepage. If you would like to update any data within homepage, adjust it here. const homepageData = { heros: [{ @@ -109,4 +108,20 @@ // Make sure arguments are correctly typed. renderHomepage(settings, homepageData, container); + + + + \ No newline at end of file