UNPKG

@kiwicom/smart-faq

Version:

Smart FAQ

403 lines (372 loc) 12.3 kB
// @flow // TODO - refactor this component - separated component for root categories & subcategories import idx from 'idx'; import * as React from 'react'; import { graphql } from 'react-relay'; import { Link, withRouter } from 'react-router-dom'; import Translate from '@kiwicom/nitro/lib/components/Translate'; import { Heading } from '@kiwicom/orbit-components'; import FAQCategoriesFullpageHeader from './FAQCategoriesFullpageHeader'; import { Desktop, Mobile } from '../../SmartFAQ/common/Responsive'; import { ExtraInfoState, categories as extraCategories, } from '../../SmartFAQ/context/ExtraInfoState'; import type { Context } from '../../SmartFAQ/context/ExtraInfoState'; import Emergencies from '../../SmartFAQ/context/Emergencies'; import UserStatus from '../../SmartFAQ/helpers/UserStatus'; import Loader from '../../SmartFAQ/common/Loader'; import ScrollableBox from '../../SmartFAQ/common/ScrollableBox'; import QueryRenderer from '../../SmartFAQ/relay/QueryRenderer'; import BaggageInfo from './FAQExtraInfo/BaggageInfo/index'; import BoardingPassesInfo from './FAQExtraInfo/BoardingPassesInfo/index'; import CustomerSupportNumber from './CustomerSupportNumber'; import FAQArticle from './FAQArticle'; import FAQCategory from './FAQCategory'; import Emergency from './emergencies/Emergency'; import EmergencyHeader from './emergencies/EmergencyHeader'; import Breadcrumbs from './breadcrumbs/Breadcrumbs'; import StaticFAQError from './StaticFAQError'; import { simpleTracker } from '../../shared/helpers/analytics/trackers'; import { GUARANTEE_ARTICLE_ID } from './ArticleDetail/ArticleContent'; import { SearchState } from '../../SmartFAQ/context/SearchState'; import type { SearchStateType } from '../../SmartFAQ/context/SearchState'; import type { FAQArticle_article } from './__generated__/FAQArticle_article.graphql'; import type { FAQCategory_category } from './__generated__/FAQCategory_category.graphql'; import type { FAQCategoryListRootQueryResponse } from './__generated__/FAQCategoryListRootQuery.graphql'; import type { FAQCategoryListSubcategoryQueryResponse } from './__generated__/FAQCategoryListSubcategoryQuery.graphql'; import { BookingState } from '../../SmartFAQ/context/BookingState'; import type { FAQSectionType, State, } from '../../SmartFAQ/context/BookingState'; import { GuaranteeChatContext } from '../../shared/context/GuaranteeChatInfo'; import { track } from '../../shared/cuckoo/tracker'; import { FullPageVersion, SidebarVersion, } from '../../SmartFAQ/common/PageVariant'; type ComponentProps = { categoryId: string | null, showGuaranteeArticle: boolean, history: { location: { pathname: string, }, }, }; type ContainerProps = { section: ?FAQSectionType, }; type Props = ComponentProps & ContainerProps; type RootQueryRendererParams = { props: ?FAQCategoryListRootQueryResponse, error: ?Error, }; type SubcategoryQueryRendererParams = { props: ?FAQCategoryListSubcategoryQueryResponse, error: ?Error, }; type CategoryFragment = {| +id: string, +originalId: string, +title: ?string, +$fragmentRefs: FAQCategory_category, |}; type FAQArticlePerexFragment = {| +id: string, +$fragmentRefs: FAQArticle_article, |}; const queryRoot = graphql` query FAQCategoryListRootQuery( $section: FAQSection! $articleId: ID! $showGuaranteeArticle: Boolean! ) { FAQArticle(id: $articleId) @include(if: $showGuaranteeArticle) { ...FAQArticle_article } FAQSection(section: $section) { id subcategories { id originalId: id(opaque: false) title ...FAQCategory_category } FAQs { id ...FAQArticle_article } } ...CustomerSupportNumberMobile } `; const querySubcategory = graphql` query FAQCategoryListSubcategoryQuery($id: ID!) { FAQCategory(id: $id) { id title subcategories { id originalId: id(opaque: false) title ...FAQCategory_category } ancestors { id ...Breadcrumbs_breadcrumbs } FAQs { id ...FAQArticle_article } } ...CustomerSupportNumberMobile } `; const categoryClicked = ( category: CategoryFragment, section: ?FAQSectionType, ) => { simpleTracker('smartFAQCategories', { action: 'clickOnCategory', section: section || '', categoryId: category.id, categoryName: category.title || '', }); track('FAQs', 'clickOnCategory', { section: section || '', categoryId: category.originalId, categoryName: category.title || '', }); }; class RawFAQCategoryList extends React.Component<Props> { renderFAQArticlePerexes = ( faqs: $ReadOnlyArray<?FAQArticlePerexFragment>, categoryId: string, ) => { return ( <div> {faqs .filter(Boolean) .map(faq => ( <FAQArticle key={faq.id} article={faq} categoryId={categoryId} /> ))} </div> ); }; renderExtraInfoCategory = activeExtraInfoCategory => { switch (activeExtraInfoCategory) { case 'baggage': return <BaggageInfo />; case 'boarding-passes': return <BoardingPassesInfo />; default: return null; } }; renderCategories = (categories: $ReadOnlyArray<CategoryFragment>) => { const { pathname } = this.props.history.location; const isBaggageRoute = pathname.includes(extraCategories.BAGGAGE); const isBoardingPassRoute = pathname.includes( extraCategories.BOARDING_PASS, ); return ( <React.Fragment> <Desktop> <UserStatus.LoggedIn> {(isBaggageRoute || isBoardingPassRoute) && ( <ExtraInfoState.Consumer> {({ activeExtraInfoCategory }: Context) => this.renderExtraInfoCategory(activeExtraInfoCategory) } </ExtraInfoState.Consumer> )} </UserStatus.LoggedIn> </Desktop> <SearchState.Consumer> {({ changeSearchText, resetQueriesCount, searchText, }: SearchStateType) => { const onSearchCancel = () => { changeSearchText(''); resetQueriesCount(); }; return ( <BookingState.Consumer> {({ FAQSection }: State) => ( <div data-cy="faq-categories"> {categories.map(category => { if (category) { return ( <Link key={category.id} to={`/faq/${category.id}`} style={{ textDecoration: 'none', display: 'block' }} onClick={() => { if (searchText.length) { onSearchCancel(); } categoryClicked(category, FAQSection); }} > <FAQCategory category={category} /> </Link> ); } return null; })} </div> )} </BookingState.Consumer> ); }} </SearchState.Consumer> </React.Fragment> ); }; renderRootCategory = (rendererProps: RootQueryRendererParams) => { if (rendererProps.error) { return <StaticFAQError />; } if (rendererProps.props) { const categories = idx(rendererProps.props, _ => _.FAQSection.subcategories) || []; const faqs = idx(rendererProps.props, _ => _.FAQSection.FAQs) || []; const categoryId = idx(rendererProps.props, _ => _.FAQSection.id) || ''; const guaranteeArticle = rendererProps.props && rendererProps.props.FAQArticle; return ( <Emergencies.Consumer> {emergencies => { const hasEmergencies = emergencies && emergencies.length > 0; return ( <ScrollableBox> {hasEmergencies && ( <EmergencyHeader styles="margin-top: 8px" title={__('smartfaq.faq.emergencies.current')} /> )} {emergencies && emergencies.map((emergency, i) => ( // eslint-disable-next-line react/no-array-index-key <Emergency key={i} emergency={emergency} /> ))} {hasEmergencies && ( <EmergencyHeader styles="margin-top: 32px" title={__('smartfaq.faq.emergencies.solve')} /> )} <FullPageVersion> <FAQCategoriesFullpageHeader /> <Heading type="title3" element="h3" spaceAfter="large"> <Translate t={__( 'smartfaq.full_page.categories.header.suggested_topics', )} /> </Heading> </FullPageVersion> {guaranteeArticle && ( <FAQArticle article={guaranteeArticle} isSearchResult /> )} {this.renderCategories(categories.filter(Boolean))} <SidebarVersion> {this.renderFAQArticlePerexes(faqs, categoryId)} </SidebarVersion> <Mobile> <UserStatus.LoggedIn> <CustomerSupportNumber data={rendererProps.props} /> </UserStatus.LoggedIn> </Mobile> </ScrollableBox> ); }} </Emergencies.Consumer> ); } return <Loader fullHeight />; }; renderSubcategory = (rendererProps: SubcategoryQueryRendererParams) => { if (rendererProps.error) { return <StaticFAQError />; } const FAQCategory = idx(rendererProps.props, _ => _.FAQCategory); if (rendererProps.props) { const categories = idx(rendererProps.props, _ => _.FAQCategory.subcategories) || []; const ancestors = idx(rendererProps.props, _ => _.FAQCategory.ancestors) || []; const currentCategory = idx( rendererProps.props, _ => _.FAQCategory.title, ); const faqs = idx(rendererProps.props, _ => _.FAQCategory.FAQs) || []; const categoryId = idx(rendererProps.props, _ => _.FAQCategory.id) || ''; if (FAQCategory === null) { return <StaticFAQError />; } return ( <React.Fragment> <div> <Breadcrumbs breadcrumbs={ancestors} currentCategory={currentCategory} /> </div> <ScrollableBox> {this.renderCategories(categories.filter(Boolean))} {this.renderFAQArticlePerexes(faqs, categoryId)} </ScrollableBox> </React.Fragment> ); } return <Loader fullHeight />; }; render() { const { categoryId, section, showGuaranteeArticle } = this.props; if (categoryId) { return ( <QueryRenderer query={querySubcategory} render={this.renderSubcategory} variables={{ id: categoryId }} /> ); } return ( <QueryRenderer query={queryRoot} render={this.renderRootCategory} variables={{ section, showGuaranteeArticle, articleId: GUARANTEE_ARTICLE_ID, }} /> ); } } const FAQCategoryList = (props: ComponentProps) => ( <BookingState.Consumer> {({ FAQSection }: State) => ( <GuaranteeChatContext.Consumer> {({ showGuaranteeChat }) => ( <RawFAQCategoryList section={FAQSection} showGuaranteeArticle={ showGuaranteeChat && props.categoryId === null } {...props} /> )} </GuaranteeChatContext.Consumer> )} </BookingState.Consumer> ); export default withRouter(FAQCategoryList);