UNPKG

@kiwicom/smart-faq

Version:

431 lines (396 loc) 12.7 kB
// @flow // TODO - refactor this component - separated component for root categories & subcategories 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 type { ContextRouter } from 'react-router-dom'; import LogContext from '@kiwicom/nitro/lib/services/log/context'; 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 { GUARANTEE_ARTICLE_ID } from './ArticleDetail/ArticleContent'; import { SearchState } from '../../SmartFAQ/context/SearchState'; import type { SearchStateType } from '../../SmartFAQ/context/SearchState'; import type { FAQArticle_article as FAQArticleType } from './__generated__/FAQArticle_article.graphql'; import type { FAQCategory_category as FAQCategoryType } 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 '../context/GuaranteeChatInfo'; import { FullPageVersion, SidebarVersion, } from '../../SmartFAQ/common/PageVariant'; import { events } from '../../const/events'; import type { log } from '../../const/events'; import features from '../../feature-toggles.json'; type OwnProps = {| ...ContextRouter, categoryId: ?number, |}; type ContainerProps = {| section: ?FAQSectionType, showGuaranteeArticle: boolean, log: log, |}; type Props = {| ...OwnProps, ...ContainerProps, |}; type RootQueryRendererParams = { props: ?FAQCategoryListRootQueryResponse, error: ?Error, }; type SubcategoryQueryRendererParams = { props: ?FAQCategoryListSubcategoryQueryResponse, error: ?Error, }; type CategoryFragment = {| +id: string, +originalId: string, +title: ?string, +$fragmentRefs: FAQCategoryType, |}; type FAQArticlePerexFragment = {| +id: string, +$fragmentRefs: FAQArticleType, |}; type FAQCategoryItemProps = { section: ?FAQSectionType, category: CategoryFragment, log: log, }; class FAQCategoryItem extends React.Component<FAQCategoryItemProps> { context: SearchStateType; static contextType = SearchState; onClick = () => { const { category, section, log } = this.props; if (this.context.searchText.length) { this.context.clearSearch(); this.context.resetQueriesCount(); } log(events.FAQ_CATEGORY_CLICK, { section: section ?? '', categoryId: category.originalId, categoryName: category.title ?? '', }); }; render() { const { category } = this.props; return ( <Link key={category.id} to={`/faq/${category.originalId}`} style={{ textDecoration: 'none', display: 'block', }} onClick={this.onClick} > <FAQCategory category={category} /> </Link> ); } } const queryRoot = graphql` query FAQCategoryListRootQuery( $section: FAQSection! $articleId: Int! $showGuaranteeArticle: Boolean! ) { FAQArticle(originalId: $articleId) @include(if: $showGuaranteeArticle) { ...FAQArticle_article } FAQSection(section: $section) { id originalId: id(opaque: false) subcategories { id originalId: id(opaque: false) title ...FAQCategory_category } FAQs { id ...FAQArticle_article } } ...CustomerSupportNumberMobile } `; const querySubcategory = graphql` query FAQCategoryListSubcategoryQuery($id: Int!) { FAQCategory(originalId: $id) { id originalId: id(opaque: false) title subcategories { id originalId: id(opaque: false) title ...FAQCategory_category } ancestors { id ...Breadcrumbs_breadcrumbs } FAQs { id ...FAQArticle_article } } ...CustomerSupportNumberMobile } `; 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 { log, history } = this.props; const { pathname } = history.location; const isBaggageRoute = pathname.includes(String(extraCategories.BAGGAGE)); const isBoardingPassRoute = pathname.includes( String(extraCategories.BOARDING_PASS), ); return ( <React.Fragment> <Desktop> <UserStatus.LoggedIn> {(isBaggageRoute || isBoardingPassRoute) && ( <ExtraInfoState.Consumer> {({ activeExtraInfoCategory }: Context) => this.renderExtraInfoCategory(activeExtraInfoCategory) } </ExtraInfoState.Consumer> )} </UserStatus.LoggedIn> </Desktop> <BookingState.Consumer> {({ FAQSection }: State) => ( <div data-cy="faq-categories"> {categories.map(category => { if (category) { return ( <FAQCategoryItem category={category} section={FAQSection} log={log} key={category.id} /> ); } return null; })} </div> )} </BookingState.Consumer> </React.Fragment> ); }; renderRootCategory = (rendererProps: RootQueryRendererParams) => { if (rendererProps.error) { return <StaticFAQError />; } if (rendererProps.props) { const categories = rendererProps.props.FAQSection?.subcategories ?? []; const faqs = rendererProps.props.FAQSection?.FAQs ?? []; const categoryId = rendererProps.props.FAQSection?.originalId ?? ''; const guaranteeArticle = rendererProps.props && rendererProps.props.FAQArticle; const Contents = ({ hasEmergencies, emergencies }) => ( <> {hasEmergencies && ( <EmergencyHeader styles="margin-top: 8px" title={__('smartfaq.faq.emergencies.current')} /> )} {emergencies && emergencies.map((emergency, i) => ( <Emergency key={i} emergency={emergency} /> ))} {hasEmergencies && ( <EmergencyHeader styles="margin-top: 32px" title={__('smartfaq.faq.emergencies.solve')} /> )} <Desktop> <FullPageVersion> {features.fullpage_loggedIn && <FAQCategoriesFullpageHeader />} <Heading type="title3" element="h3" spaceAfter="large"> <Translate t="smartfaq.full_page.categories.header.suggested_topics" /> </Heading> </FullPageVersion> </Desktop> {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> </> ); return ( <Emergencies.Consumer> {emergencies => { const hasEmergencies = emergencies && emergencies.length > 0; return ( <> <FullPageVersion> <ScrollableBox disabled={true}> <Contents hasEmergencies={hasEmergencies} emergencies={emergencies} /> </ScrollableBox> </FullPageVersion> <SidebarVersion> <ScrollableBox> <Contents hasEmergencies={hasEmergencies} emergencies={emergencies} /> </ScrollableBox> </SidebarVersion> </> ); }} </Emergencies.Consumer> ); } return ( <SidebarVersion> <Loader fullHeight /> </SidebarVersion> ); }; renderSubcategory = (rendererProps: SubcategoryQueryRendererParams) => { if (rendererProps.error) { return <StaticFAQError />; } const FAQCategory = rendererProps.props?.FAQCategory; if (rendererProps.props) { const categories = rendererProps.props.FAQCategory?.subcategories ?? []; const ancestors = rendererProps.props.FAQCategory?.ancestors ?? []; const currentCategory = rendererProps.props.FAQCategory?.title; const faqs = rendererProps.props.FAQCategory?.FAQs ?? []; const categoryId = rendererProps.props.FAQCategory?.originalId ?? ''; if (FAQCategory === null) { return <StaticFAQError />; } return ( <> <div> <Breadcrumbs breadcrumbs={ancestors} currentCategory={currentCategory} /> </div> <ScrollableBox disabled> {this.renderCategories(categories.filter(Boolean))} {this.renderFAQArticlePerexes(faqs, categoryId)} </ScrollableBox> </> ); } return <Loader fullHeight />; }; render() { const { categoryId, section, showGuaranteeArticle, match } = this.props; const { categoryId: ssrCategoryId } = match?.params ?? {}; if (categoryId || ssrCategoryId) { return ( <QueryRenderer query={querySubcategory} render={this.renderSubcategory} variables={{ id: Number(categoryId ?? ssrCategoryId) }} /> ); } return ( <QueryRenderer query={queryRoot} render={this.renderRootCategory} variables={{ section, showGuaranteeArticle, articleId: GUARANTEE_ARTICLE_ID, }} /> ); } } const FAQCategoryList = (props: OwnProps) => ( <BookingState.Consumer> {({ FAQSection }) => ( <GuaranteeChatContext.Consumer> {({ showGuaranteeChat }) => ( <LogContext.Consumer> {({ log }) => ( <RawFAQCategoryList log={log} section={FAQSection} showGuaranteeArticle={ showGuaranteeChat && props.categoryId === null } {...props} /> )} </LogContext.Consumer> )} </GuaranteeChatContext.Consumer> )} </BookingState.Consumer> ); export default withRouter(FAQCategoryList);