From e8c3c3cfb8d0edf50b0ba86ebcf9d758138bda40 Mon Sep 17 00:00:00 2001 From: Radoslav Zeman <19418224+radoslavzeman@users.noreply.github.com> Date: Mon, 23 Sep 2024 10:43:06 +0200 Subject: [PATCH] #1323 Add Faqs as content type (#1346) * #1323 Add Faq and Faq Category content types to Strapi * #1323 Implement FE sections for Faqs and Faq Categories * #1323 Fix PR comments --- .../components/common/FaqsGroup/FaqsGroup.tsx | 31 ++ next/components/layouts/Sections.tsx | 8 + .../sections/FaqCategoriesSection.tsx | 44 ++ next/components/sections/FaqsSection.tsx | 31 ++ next/services/graphql/index.ts | 493 ++++++++++++++++++ next/services/graphql/queries/Faqs.graphql | 20 + .../services/graphql/queries/Sections.graphql | 28 + strapi/schema.graphql | 164 +++++- .../content-types/faq-category/schema.json | 44 ++ .../faq-category/controllers/faq-category.ts | 7 + .../api/faq-category/routes/faq-category.ts | 7 + .../api/faq-category/services/faq-category.ts | 7 + .../src/api/faq/content-types/faq/schema.json | 43 ++ strapi/src/api/faq/controllers/faq.ts | 7 + strapi/src/api/faq/routes/faq.ts | 7 + strapi/src/api/faq/services/faq.ts | 7 + .../api/page/content-types/page/schema.json | 2 + .../components/sections/faq-categories.json | 20 + strapi/src/components/sections/faqs.json | 21 + 19 files changed, 989 insertions(+), 2 deletions(-) create mode 100644 next/components/common/FaqsGroup/FaqsGroup.tsx create mode 100644 next/components/sections/FaqCategoriesSection.tsx create mode 100644 next/components/sections/FaqsSection.tsx create mode 100644 next/services/graphql/queries/Faqs.graphql create mode 100644 strapi/src/api/faq-category/content-types/faq-category/schema.json create mode 100644 strapi/src/api/faq-category/controllers/faq-category.ts create mode 100644 strapi/src/api/faq-category/routes/faq-category.ts create mode 100644 strapi/src/api/faq-category/services/faq-category.ts create mode 100644 strapi/src/api/faq/content-types/faq/schema.json create mode 100644 strapi/src/api/faq/controllers/faq.ts create mode 100644 strapi/src/api/faq/routes/faq.ts create mode 100644 strapi/src/api/faq/services/faq.ts create mode 100644 strapi/src/components/sections/faq-categories.json create mode 100644 strapi/src/components/sections/faqs.json diff --git a/next/components/common/FaqsGroup/FaqsGroup.tsx b/next/components/common/FaqsGroup/FaqsGroup.tsx new file mode 100644 index 000000000..0d5c867ab --- /dev/null +++ b/next/components/common/FaqsGroup/FaqsGroup.tsx @@ -0,0 +1,31 @@ +import React from 'react' + +import Accordion from '@/components/common/Accordion/Accordion' +import Markdown from '@/components/formatting/Markdown/Markdown' +import { FaqEntityFragment } from '@/services/graphql' +import { isDefined } from '@/utils/isDefined' + +export type FaqsGroupProps = { + faqs: FaqEntityFragment[] +} + +const FaqsGroup = ({ faqs }: FaqsGroupProps) => { + return ( +
+ {faqs + .map((faq, index) => { + if (!faq.attributes) return null + + return ( + // eslint-disable-next-line react/no-array-index-key + + + + ) + }) + .filter(isDefined)} +
+ ) +} + +export default FaqsGroup diff --git a/next/components/layouts/Sections.tsx b/next/components/layouts/Sections.tsx index 1612bccfe..7794dcbc1 100644 --- a/next/components/layouts/Sections.tsx +++ b/next/components/layouts/Sections.tsx @@ -13,6 +13,8 @@ import ColumnedTextSection from '@/components/sections/ColumnedTextSection' import ComparisonSection from '@/components/sections/ComparisonSection' import ContactsSection from '@/components/sections/ContactsSection' import DividerSection from '@/components/sections/DividerSection' +import FaqCategoriesSection from '@/components/sections/FaqCategoriesSection' +import FaqsSection from '@/components/sections/FaqsSection' import FeaturedBlogPostsSection from '@/components/sections/FeaturedBlogPostsSection' import FileListSection from '@/components/sections/FileListSection' import GallerySection from '@/components/sections/GallerySection' @@ -120,6 +122,12 @@ const SectionContent = ({ section }: { section: SectionsFragment }) => { case 'ComponentSectionsTestimonials': return + case 'ComponentSectionsFaqs': + return + + case 'ComponentSectionsFaqCategories': + return + default: return null } diff --git a/next/components/sections/FaqCategoriesSection.tsx b/next/components/sections/FaqCategoriesSection.tsx new file mode 100644 index 000000000..02254c783 --- /dev/null +++ b/next/components/sections/FaqCategoriesSection.tsx @@ -0,0 +1,44 @@ +import { Typography } from '@bratislava/component-library' +import React from 'react' + +import FaqsGroup from '@/components/common/FaqsGroup/FaqsGroup' +import { FaqCategoriesSectionFragment } from '@/services/graphql' +import { isDefined } from '@/utils/isDefined' + +type Props = { + section: FaqCategoriesSectionFragment +} + +const FaqCategoriesSection = ({ section }: Props) => { + const { title, text, faqCategories } = section ?? {} + + const filteredFaqCategories = faqCategories?.data.filter(isDefined) ?? [] + + return ( +
+ {title || text ? ( +
+ {title ? {title} : null} + {text ? {text} : null} +
+ ) : null} + {filteredFaqCategories + .map((faqCategory) => { + if (!faqCategory.attributes) return null + + const { title: categoryTitle, faqs, slug } = faqCategory.attributes + const filteredFaqs = faqs?.data.filter(isDefined) ?? [] + + return ( +
+ {categoryTitle} + +
+ ) + }) + .filter(isDefined)} +
+ ) +} + +export default FaqCategoriesSection diff --git a/next/components/sections/FaqsSection.tsx b/next/components/sections/FaqsSection.tsx new file mode 100644 index 000000000..62910652a --- /dev/null +++ b/next/components/sections/FaqsSection.tsx @@ -0,0 +1,31 @@ +import { Typography } from '@bratislava/component-library' +import React from 'react' + +import FaqsGroup from '@/components/common/FaqsGroup/FaqsGroup' +import { FaqsSectionFragment } from '@/services/graphql' +import { isDefined } from '@/utils/isDefined' + +type Props = { + section: FaqsSectionFragment +} + +const FaqsSection = ({ section }: Props) => { + const { title, text, faqs } = section ?? {} + + const filteredFaqs = faqs?.data.filter(isDefined) ?? [] + + return ( +
+ {title || text ? ( +
+ {title ? {title} : null} + {text ? {text} : null} +
+ ) : null} + + +
+ ) +} + +export default FaqsSection diff --git a/next/services/graphql/index.ts b/next/services/graphql/index.ts index f509c35cb..72dffdaa9 100644 --- a/next/services/graphql/index.ts +++ b/next/services/graphql/index.ts @@ -1130,6 +1130,36 @@ export type ComponentSectionsDivider = { style?: Maybe } +export type ComponentSectionsFaqCategories = { + __typename?: 'ComponentSectionsFaqCategories' + faqCategories?: Maybe + id: Scalars['ID']['output'] + text?: Maybe + title?: Maybe +} + +export type ComponentSectionsFaqCategoriesFaqCategoriesArgs = { + filters?: InputMaybe + pagination?: InputMaybe + publicationState?: InputMaybe + sort?: InputMaybe>> +} + +export type ComponentSectionsFaqs = { + __typename?: 'ComponentSectionsFaqs' + faqs?: Maybe + id: Scalars['ID']['output'] + text?: Maybe + title?: Maybe +} + +export type ComponentSectionsFaqsFaqsArgs = { + filters?: InputMaybe + pagination?: InputMaybe + publicationState?: InputMaybe + sort?: InputMaybe>> +} + export type ComponentSectionsFeaturedBlogPosts = { __typename?: 'ComponentSectionsFeaturedBlogPosts' first_blog?: Maybe @@ -1838,6 +1868,139 @@ export type Error = { message?: Maybe } +export type Faq = { + __typename?: 'Faq' + body?: Maybe + createdAt?: Maybe + faqCategory?: Maybe + locale?: Maybe + localizations?: Maybe + publishedAt?: Maybe + title: Scalars['String']['output'] + updatedAt?: Maybe +} + +export type FaqLocalizationsArgs = { + filters?: InputMaybe + pagination?: InputMaybe + publicationState?: InputMaybe + sort?: InputMaybe>> +} + +export type FaqCategory = { + __typename?: 'FaqCategory' + createdAt?: Maybe + faqs?: Maybe + locale?: Maybe + localizations?: Maybe + publishedAt?: Maybe + slug: Scalars['String']['output'] + title: Scalars['String']['output'] + updatedAt?: Maybe +} + +export type FaqCategoryFaqsArgs = { + filters?: InputMaybe + pagination?: InputMaybe + publicationState?: InputMaybe + sort?: InputMaybe>> +} + +export type FaqCategoryLocalizationsArgs = { + filters?: InputMaybe + pagination?: InputMaybe + publicationState?: InputMaybe + sort?: InputMaybe>> +} + +export type FaqCategoryEntity = { + __typename?: 'FaqCategoryEntity' + attributes?: Maybe + id?: Maybe +} + +export type FaqCategoryEntityResponse = { + __typename?: 'FaqCategoryEntityResponse' + data?: Maybe +} + +export type FaqCategoryEntityResponseCollection = { + __typename?: 'FaqCategoryEntityResponseCollection' + data: Array + meta: ResponseCollectionMeta +} + +export type FaqCategoryFiltersInput = { + and?: InputMaybe>> + createdAt?: InputMaybe + faqs?: InputMaybe + id?: InputMaybe + locale?: InputMaybe + localizations?: InputMaybe + not?: InputMaybe + or?: InputMaybe>> + publishedAt?: InputMaybe + slug?: InputMaybe + title?: InputMaybe + updatedAt?: InputMaybe +} + +export type FaqCategoryInput = { + faqs?: InputMaybe>> + publishedAt?: InputMaybe + slug?: InputMaybe + title?: InputMaybe +} + +export type FaqCategoryRelationResponseCollection = { + __typename?: 'FaqCategoryRelationResponseCollection' + data: Array +} + +export type FaqEntity = { + __typename?: 'FaqEntity' + attributes?: Maybe + id?: Maybe +} + +export type FaqEntityResponse = { + __typename?: 'FaqEntityResponse' + data?: Maybe +} + +export type FaqEntityResponseCollection = { + __typename?: 'FaqEntityResponseCollection' + data: Array + meta: ResponseCollectionMeta +} + +export type FaqFiltersInput = { + and?: InputMaybe>> + body?: InputMaybe + createdAt?: InputMaybe + faqCategory?: InputMaybe + id?: InputMaybe + locale?: InputMaybe + localizations?: InputMaybe + not?: InputMaybe + or?: InputMaybe>> + publishedAt?: InputMaybe + title?: InputMaybe + updatedAt?: InputMaybe +} + +export type FaqInput = { + body?: InputMaybe + faqCategory?: InputMaybe + publishedAt?: InputMaybe + title?: InputMaybe +} + +export type FaqRelationResponseCollection = { + __typename?: 'FaqRelationResponseCollection' + data: Array +} + export type FileInfoInput = { alternativeText?: InputMaybe caption?: InputMaybe @@ -2019,6 +2182,8 @@ export type GenericMorph = | ComponentSectionsComparisonSection | ComponentSectionsContactsSection | ComponentSectionsDivider + | ComponentSectionsFaqCategories + | ComponentSectionsFaqs | ComponentSectionsFeaturedBlogPosts | ComponentSectionsFileList | ComponentSectionsGallery @@ -2047,6 +2212,8 @@ export type GenericMorph = | ComponentSectionsVideos | ComponentSectionsWaves | ComponentTaxAdministratorsTaxAdministrator + | Faq + | FaqCategory | Footer | General | Homepage @@ -2523,6 +2690,10 @@ export type Mutation = { createAlertLocalization?: Maybe createBlogPost?: Maybe createBlogPostLocalization?: Maybe + createFaq?: Maybe + createFaqCategory?: Maybe + createFaqCategoryLocalization?: Maybe + createFaqLocalization?: Maybe createFooterLocalization?: Maybe createGeneralLocalization?: Maybe createHomepageLocalization?: Maybe @@ -2550,6 +2721,8 @@ export type Mutation = { createVzn?: Maybe deleteAlert?: Maybe deleteBlogPost?: Maybe + deleteFaq?: Maybe + deleteFaqCategory?: Maybe deleteFooter?: Maybe deleteGeneral?: Maybe deleteHomepage?: Maybe @@ -2583,6 +2756,8 @@ export type Mutation = { resetPassword?: Maybe updateAlert?: Maybe updateBlogPost?: Maybe + updateFaq?: Maybe + updateFaqCategory?: Maybe updateFileInfo: UploadFileEntityResponse updateFooter?: Maybe updateGeneral?: Maybe @@ -2630,6 +2805,28 @@ export type MutationCreateBlogPostLocalizationArgs = { locale?: InputMaybe } +export type MutationCreateFaqArgs = { + data: FaqInput + locale?: InputMaybe +} + +export type MutationCreateFaqCategoryArgs = { + data: FaqCategoryInput + locale?: InputMaybe +} + +export type MutationCreateFaqCategoryLocalizationArgs = { + data?: InputMaybe + id?: InputMaybe + locale?: InputMaybe +} + +export type MutationCreateFaqLocalizationArgs = { + data?: InputMaybe + id?: InputMaybe + locale?: InputMaybe +} + export type MutationCreateFooterLocalizationArgs = { data?: InputMaybe id?: InputMaybe @@ -2757,6 +2954,16 @@ export type MutationDeleteBlogPostArgs = { locale?: InputMaybe } +export type MutationDeleteFaqArgs = { + id: Scalars['ID']['input'] + locale?: InputMaybe +} + +export type MutationDeleteFaqCategoryArgs = { + id: Scalars['ID']['input'] + locale?: InputMaybe +} + export type MutationDeleteFooterArgs = { locale?: InputMaybe } @@ -2875,6 +3082,18 @@ export type MutationUpdateBlogPostArgs = { locale?: InputMaybe } +export type MutationUpdateFaqArgs = { + data: FaqInput + id: Scalars['ID']['input'] + locale?: InputMaybe +} + +export type MutationUpdateFaqCategoryArgs = { + data: FaqCategoryInput + id: Scalars['ID']['input'] + locale?: InputMaybe +} + export type MutationUpdateFileInfoArgs = { id: Scalars['ID']['input'] info?: InputMaybe @@ -3194,6 +3413,8 @@ export type PageSectionsDynamicZone = | ComponentSectionsComparisonSection | ComponentSectionsContactsSection | ComponentSectionsDivider + | ComponentSectionsFaqCategories + | ComponentSectionsFaqs | ComponentSectionsFeaturedBlogPosts | ComponentSectionsFileList | ComponentSectionsGallery @@ -3314,6 +3535,10 @@ export type Query = { alert?: Maybe blogPost?: Maybe blogPosts?: Maybe + faq?: Maybe + faqCategories?: Maybe + faqCategory?: Maybe + faqs?: Maybe footer?: Maybe general?: Maybe homepage?: Maybe @@ -3367,6 +3592,32 @@ export type QueryBlogPostsArgs = { sort?: InputMaybe>> } +export type QueryFaqArgs = { + id?: InputMaybe + locale?: InputMaybe +} + +export type QueryFaqCategoriesArgs = { + filters?: InputMaybe + locale?: InputMaybe + pagination?: InputMaybe + publicationState?: InputMaybe + sort?: InputMaybe>> +} + +export type QueryFaqCategoryArgs = { + id?: InputMaybe + locale?: InputMaybe +} + +export type QueryFaqsArgs = { + filters?: InputMaybe + locale?: InputMaybe + pagination?: InputMaybe + publicationState?: InputMaybe + sort?: InputMaybe>> +} + export type QueryFooterArgs = { locale?: InputMaybe publicationState?: InputMaybe @@ -5617,6 +5868,30 @@ export type PageCategoryEntityFragment = { } | null } +export type FaqCategoryEntityFragment = { + __typename?: 'FaqCategoryEntity' + id?: string | null + attributes?: { + __typename?: 'FaqCategory' + title: string + slug: string + faqs?: { + __typename?: 'FaqRelationResponseCollection' + data: Array<{ + __typename?: 'FaqEntity' + id?: string | null + attributes?: { __typename?: 'Faq'; title: string; body?: string | null } | null + }> + } | null + } | null +} + +export type FaqEntityFragment = { + __typename?: 'FaqEntity' + id?: string | null + attributes?: { __typename?: 'Faq'; title: string; body?: string | null } | null +} + export type UploadImageSrcEntityFragment = { __typename?: 'UploadFileEntity' id?: string | null @@ -8398,6 +8673,48 @@ export type PageBySlugQuery = { hasBackground?: boolean | null style?: Enum_Componentsectionsdivider_Style | null } + | { + __typename: 'ComponentSectionsFaqCategories' + title?: string | null + text?: string | null + faqCategories?: { + __typename?: 'FaqCategoryRelationResponseCollection' + data: Array<{ + __typename?: 'FaqCategoryEntity' + id?: string | null + attributes?: { + __typename?: 'FaqCategory' + title: string + slug: string + faqs?: { + __typename?: 'FaqRelationResponseCollection' + data: Array<{ + __typename?: 'FaqEntity' + id?: string | null + attributes?: { + __typename?: 'Faq' + title: string + body?: string | null + } | null + }> + } | null + } | null + }> + } | null + } + | { + __typename: 'ComponentSectionsFaqs' + title?: string | null + text?: string | null + faqs?: { + __typename?: 'FaqRelationResponseCollection' + data: Array<{ + __typename?: 'FaqEntity' + id?: string | null + attributes?: { __typename?: 'Faq'; title: string; body?: string | null } | null + }> + } | null + } | { __typename: 'ComponentSectionsFeaturedBlogPosts' id: string @@ -9462,6 +9779,44 @@ export type PageEntityFragment = { hasBackground?: boolean | null style?: Enum_Componentsectionsdivider_Style | null } + | { + __typename: 'ComponentSectionsFaqCategories' + title?: string | null + text?: string | null + faqCategories?: { + __typename?: 'FaqCategoryRelationResponseCollection' + data: Array<{ + __typename?: 'FaqCategoryEntity' + id?: string | null + attributes?: { + __typename?: 'FaqCategory' + title: string + slug: string + faqs?: { + __typename?: 'FaqRelationResponseCollection' + data: Array<{ + __typename?: 'FaqEntity' + id?: string | null + attributes?: { __typename?: 'Faq'; title: string; body?: string | null } | null + }> + } | null + } | null + }> + } | null + } + | { + __typename: 'ComponentSectionsFaqs' + title?: string | null + text?: string | null + faqs?: { + __typename?: 'FaqRelationResponseCollection' + data: Array<{ + __typename?: 'FaqEntity' + id?: string | null + attributes?: { __typename?: 'Faq'; title: string; body?: string | null } | null + }> + } | null + } | { __typename: 'ComponentSectionsFeaturedBlogPosts' id: string @@ -12115,6 +12470,46 @@ export type RegulationsSectionFragment = { } | null } +export type FaqsSectionFragment = { + __typename?: 'ComponentSectionsFaqs' + title?: string | null + text?: string | null + faqs?: { + __typename?: 'FaqRelationResponseCollection' + data: Array<{ + __typename?: 'FaqEntity' + id?: string | null + attributes?: { __typename?: 'Faq'; title: string; body?: string | null } | null + }> + } | null +} + +export type FaqCategoriesSectionFragment = { + __typename?: 'ComponentSectionsFaqCategories' + title?: string | null + text?: string | null + faqCategories?: { + __typename?: 'FaqCategoryRelationResponseCollection' + data: Array<{ + __typename?: 'FaqCategoryEntity' + id?: string | null + attributes?: { + __typename?: 'FaqCategory' + title: string + slug: string + faqs?: { + __typename?: 'FaqRelationResponseCollection' + data: Array<{ + __typename?: 'FaqEntity' + id?: string | null + attributes?: { __typename?: 'Faq'; title: string; body?: string | null } | null + }> + } | null + } | null + }> + } | null +} + type Sections_ComponentSectionsAccordion_Fragment = { __typename: 'ComponentSectionsAccordion' title?: string | null @@ -12361,6 +12756,46 @@ type Sections_ComponentSectionsDivider_Fragment = { style?: Enum_Componentsectionsdivider_Style | null } +type Sections_ComponentSectionsFaqCategories_Fragment = { + __typename: 'ComponentSectionsFaqCategories' + title?: string | null + text?: string | null + faqCategories?: { + __typename?: 'FaqCategoryRelationResponseCollection' + data: Array<{ + __typename?: 'FaqCategoryEntity' + id?: string | null + attributes?: { + __typename?: 'FaqCategory' + title: string + slug: string + faqs?: { + __typename?: 'FaqRelationResponseCollection' + data: Array<{ + __typename?: 'FaqEntity' + id?: string | null + attributes?: { __typename?: 'Faq'; title: string; body?: string | null } | null + }> + } | null + } | null + }> + } | null +} + +type Sections_ComponentSectionsFaqs_Fragment = { + __typename: 'ComponentSectionsFaqs' + title?: string | null + text?: string | null + faqs?: { + __typename?: 'FaqRelationResponseCollection' + data: Array<{ + __typename?: 'FaqEntity' + id?: string | null + attributes?: { __typename?: 'Faq'; title: string; body?: string | null } | null + }> + } | null +} + type Sections_ComponentSectionsFeaturedBlogPosts_Fragment = { __typename: 'ComponentSectionsFeaturedBlogPosts' id: string @@ -12971,6 +13406,8 @@ export type SectionsFragment = | Sections_ComponentSectionsComparisonSection_Fragment | Sections_ComponentSectionsContactsSection_Fragment | Sections_ComponentSectionsDivider_Fragment + | Sections_ComponentSectionsFaqCategories_Fragment + | Sections_ComponentSectionsFaqs_Fragment | Sections_ComponentSectionsFeaturedBlogPosts_Fragment | Sections_ComponentSectionsFileList_Fragment | Sections_ComponentSectionsGallery_Fragment @@ -13882,6 +14319,54 @@ export const TestimonialsSectionFragmentDoc = gql` } ${TestimonialItemBlockFragmentDoc} ` +export const FaqEntityFragmentDoc = gql` + fragment FaqEntity on FaqEntity { + id + attributes { + title + body + } + } +` +export const FaqsSectionFragmentDoc = gql` + fragment FaqsSection on ComponentSectionsFaqs { + title + text + faqs { + data { + ...FaqEntity + } + } + } + ${FaqEntityFragmentDoc} +` +export const FaqCategoryEntityFragmentDoc = gql` + fragment FaqCategoryEntity on FaqCategoryEntity { + id + attributes { + title + slug + faqs { + data { + ...FaqEntity + } + } + } + } + ${FaqEntityFragmentDoc} +` +export const FaqCategoriesSectionFragmentDoc = gql` + fragment FaqCategoriesSection on ComponentSectionsFaqCategories { + title + text + faqCategories { + data { + ...FaqCategoryEntity + } + } + } + ${FaqCategoryEntityFragmentDoc} +` export const SectionsFragmentDoc = gql` fragment Sections on PageSectionsDynamicZone { __typename @@ -13972,6 +14457,12 @@ export const SectionsFragmentDoc = gql` ... on ComponentSectionsTestimonials { ...TestimonialsSection } + ... on ComponentSectionsFaqs { + ...FaqsSection + } + ... on ComponentSectionsFaqCategories { + ...FaqCategoriesSection + } } ${IconTitleDescSectionFragmentDoc} ${DividerSectionFragmentDoc} @@ -14002,6 +14493,8 @@ export const SectionsFragmentDoc = gql` ${RegulationsListSectionFragmentDoc} ${RegulationsSectionFragmentDoc} ${TestimonialsSectionFragmentDoc} + ${FaqsSectionFragmentDoc} + ${FaqCategoriesSectionFragmentDoc} ` export const BlogPostEntityFragmentDoc = gql` fragment BlogPostEntity on BlogPostEntity { diff --git a/next/services/graphql/queries/Faqs.graphql b/next/services/graphql/queries/Faqs.graphql new file mode 100644 index 000000000..c4e26e002 --- /dev/null +++ b/next/services/graphql/queries/Faqs.graphql @@ -0,0 +1,20 @@ +fragment FaqCategoryEntity on FaqCategoryEntity { + id + attributes { + title + slug + faqs { + data { + ...FaqEntity + } + } + } +} + +fragment FaqEntity on FaqEntity { + id + attributes { + title + body + } +} diff --git a/next/services/graphql/queries/Sections.graphql b/next/services/graphql/queries/Sections.graphql index 1e4012339..e1bafbe3f 100644 --- a/next/services/graphql/queries/Sections.graphql +++ b/next/services/graphql/queries/Sections.graphql @@ -446,6 +446,26 @@ fragment RegulationsSection on ComponentSectionsRegulations { } } +fragment FaqsSection on ComponentSectionsFaqs { + title + text + faqs { + data { + ...FaqEntity + } + } +} + +fragment FaqCategoriesSection on ComponentSectionsFaqCategories { + title + text + faqCategories { + data { + ...FaqCategoryEntity + } + } +} + fragment Sections on PageSectionsDynamicZone { __typename @@ -566,6 +586,14 @@ fragment Sections on PageSectionsDynamicZone { ... on ComponentSectionsTestimonials { ...TestimonialsSection } + + ... on ComponentSectionsFaqs { + ...FaqsSection + } + + ... on ComponentSectionsFaqCategories { + ...FaqCategoriesSection + } } fragment SubpageListPageHeaderSection on ComponentSectionsSubpageList { diff --git a/strapi/schema.graphql b/strapi/schema.graphql index be3289e4d..a6afb90f7 100644 --- a/strapi/schema.graphql +++ b/strapi/schema.graphql @@ -1303,6 +1303,52 @@ input ComponentSectionsDividerInput { style: ENUM_COMPONENTSECTIONSDIVIDER_STYLE } +type ComponentSectionsFaqCategories { + faqCategories(filters: FaqCategoryFiltersInput, pagination: PaginationArg = {}, publicationState: PublicationState = LIVE, sort: [String] = []): FaqCategoryRelationResponseCollection + id: ID! + text: String + title: String +} + +input ComponentSectionsFaqCategoriesFiltersInput { + and: [ComponentSectionsFaqCategoriesFiltersInput] + faqCategories: FaqCategoryFiltersInput + not: ComponentSectionsFaqCategoriesFiltersInput + or: [ComponentSectionsFaqCategoriesFiltersInput] + text: StringFilterInput + title: StringFilterInput +} + +input ComponentSectionsFaqCategoriesInput { + faqCategories: [ID] + id: ID + text: String + title: String +} + +type ComponentSectionsFaqs { + faqs(filters: FaqFiltersInput, pagination: PaginationArg = {}, publicationState: PublicationState = LIVE, sort: [String] = []): FaqRelationResponseCollection + id: ID! + text: String + title: String +} + +input ComponentSectionsFaqsFiltersInput { + and: [ComponentSectionsFaqsFiltersInput] + faqs: FaqFiltersInput + not: ComponentSectionsFaqsFiltersInput + or: [ComponentSectionsFaqsFiltersInput] + text: StringFilterInput + title: StringFilterInput +} + +input ComponentSectionsFaqsInput { + faqs: [ID] + id: ID + text: String + title: String +} + type ComponentSectionsFeaturedBlogPosts { first_blog: BlogPostEntityResponse id: ID! @@ -2293,6 +2339,108 @@ type Error { message: String } +type Faq { + body: String + createdAt: DateTime + faqCategory: FaqCategoryEntityResponse + locale: String + localizations(filters: FaqFiltersInput, pagination: PaginationArg = {}, publicationState: PublicationState = LIVE, sort: [String] = []): FaqRelationResponseCollection + publishedAt: DateTime + title: String! + updatedAt: DateTime +} + +type FaqCategory { + createdAt: DateTime + faqs(filters: FaqFiltersInput, pagination: PaginationArg = {}, publicationState: PublicationState = LIVE, sort: [String] = []): FaqRelationResponseCollection + locale: String + localizations(filters: FaqCategoryFiltersInput, pagination: PaginationArg = {}, publicationState: PublicationState = LIVE, sort: [String] = []): FaqCategoryRelationResponseCollection + publishedAt: DateTime + slug: String! + title: String! + updatedAt: DateTime +} + +type FaqCategoryEntity { + attributes: FaqCategory + id: ID +} + +type FaqCategoryEntityResponse { + data: FaqCategoryEntity +} + +type FaqCategoryEntityResponseCollection { + data: [FaqCategoryEntity!]! + meta: ResponseCollectionMeta! +} + +input FaqCategoryFiltersInput { + and: [FaqCategoryFiltersInput] + createdAt: DateTimeFilterInput + faqs: FaqFiltersInput + id: IDFilterInput + locale: StringFilterInput + localizations: FaqCategoryFiltersInput + not: FaqCategoryFiltersInput + or: [FaqCategoryFiltersInput] + publishedAt: DateTimeFilterInput + slug: StringFilterInput + title: StringFilterInput + updatedAt: DateTimeFilterInput +} + +input FaqCategoryInput { + faqs: [ID] + publishedAt: DateTime + slug: String + title: String +} + +type FaqCategoryRelationResponseCollection { + data: [FaqCategoryEntity!]! +} + +type FaqEntity { + attributes: Faq + id: ID +} + +type FaqEntityResponse { + data: FaqEntity +} + +type FaqEntityResponseCollection { + data: [FaqEntity!]! + meta: ResponseCollectionMeta! +} + +input FaqFiltersInput { + and: [FaqFiltersInput] + body: StringFilterInput + createdAt: DateTimeFilterInput + faqCategory: FaqCategoryFiltersInput + id: IDFilterInput + locale: StringFilterInput + localizations: FaqFiltersInput + not: FaqFiltersInput + or: [FaqFiltersInput] + publishedAt: DateTimeFilterInput + title: StringFilterInput + updatedAt: DateTimeFilterInput +} + +input FaqInput { + body: String + faqCategory: ID + publishedAt: DateTime + title: String +} + +type FaqRelationResponseCollection { + data: [FaqEntity!]! +} + input FileInfoInput { alternativeText: String caption: String @@ -2444,7 +2592,7 @@ type GeneralRelationResponseCollection { data: [GeneralEntity!]! } -union GenericMorph = Alert | BlogPost | ComponentAccordionItemsFlatText | ComponentAccordionItemsInstitution | ComponentAccordionItemsInstitutionNarrow | ComponentBlocksBlogPostLink | ComponentBlocksBookmarkLink | ComponentBlocksCommonLink | ComponentBlocksComparisonCard | ComponentBlocksComparisonItem | ComponentBlocksContactCard | ComponentBlocksDocListExtensions | ComponentBlocksFile | ComponentBlocksFileItem | ComponentBlocksFooterColumn | ComponentBlocksFooterContactItem | ComponentBlocksFooterSection | ComponentBlocksGalleryItem | ComponentBlocksHomepageBookmark | ComponentBlocksHomepageHighlightsItem | ComponentBlocksIconWithTitleAndDescription | ComponentBlocksInBa | ComponentBlocksNumericalListItem | ComponentBlocksPageLink | ComponentBlocksProsAndConsCard | ComponentBlocksSpaceInfo | ComponentBlocksSubpage | ComponentBlocksTestimonialItem | ComponentBlocksTimelineItem | ComponentBlocksTopServicesItem | ComponentBlocksVideo | ComponentGeneralHeader | ComponentGeneralHeaderLink | ComponentMenuMenuItem | ComponentMenuMenuLink | ComponentMenuMenuSection | ComponentOsItemsAdvancedAccordionDepartment | ComponentOsItemsAdvancedAccordionItem | ComponentOsItemsAdvancedAccordionSubItem | ComponentOsItemsAdvancedAccordionSubSubItem | ComponentSectionsAccordion | ComponentSectionsBanner | ComponentSectionsBlogPostsByCategory | ComponentSectionsBlogPostsByTags | ComponentSectionsBlogPostsList | ComponentSectionsCalculator | ComponentSectionsColumnedText | ComponentSectionsComparisonSection | ComponentSectionsContactsSection | ComponentSectionsDivider | ComponentSectionsFeaturedBlogPosts | ComponentSectionsFileList | ComponentSectionsGallery | ComponentSectionsHomepageEvents | ComponentSectionsHomepageHighlights | ComponentSectionsHomepageMayorAndCouncil | ComponentSectionsHomepageTabs | ComponentSectionsIconTitleDesc | ComponentSectionsIframe | ComponentSectionsInbaArticlesList | ComponentSectionsInbaReleases | ComponentSectionsLinks | ComponentSectionsNarrowText | ComponentSectionsNumericalList | ComponentSectionsOfficialBoard | ComponentSectionsOrganizationalStructure | ComponentSectionsProsAndConsSection | ComponentSectionsRegulations | ComponentSectionsRegulationsList | ComponentSectionsSpace | ComponentSectionsSubpageList | ComponentSectionsTestimonials | ComponentSectionsTextWithImage | ComponentSectionsTimeline | ComponentSectionsTopServices | ComponentSectionsVideos | ComponentSectionsWaves | ComponentTaxAdministratorsTaxAdministrator | Footer | General | Homepage | I18NLocale | InbaArticle | InbaRelease | InbaTag | Menu | Page | PageCategory | PageSubcategory | Regulation | Tag | TaxAdministratorsList | UploadFile | UploadFolder | UsersPermissionsPermission | UsersPermissionsRole | UsersPermissionsUser | Vzn +union GenericMorph = Alert | BlogPost | ComponentAccordionItemsFlatText | ComponentAccordionItemsInstitution | ComponentAccordionItemsInstitutionNarrow | ComponentBlocksBlogPostLink | ComponentBlocksBookmarkLink | ComponentBlocksCommonLink | ComponentBlocksComparisonCard | ComponentBlocksComparisonItem | ComponentBlocksContactCard | ComponentBlocksDocListExtensions | ComponentBlocksFile | ComponentBlocksFileItem | ComponentBlocksFooterColumn | ComponentBlocksFooterContactItem | ComponentBlocksFooterSection | ComponentBlocksGalleryItem | ComponentBlocksHomepageBookmark | ComponentBlocksHomepageHighlightsItem | ComponentBlocksIconWithTitleAndDescription | ComponentBlocksInBa | ComponentBlocksNumericalListItem | ComponentBlocksPageLink | ComponentBlocksProsAndConsCard | ComponentBlocksSpaceInfo | ComponentBlocksSubpage | ComponentBlocksTestimonialItem | ComponentBlocksTimelineItem | ComponentBlocksTopServicesItem | ComponentBlocksVideo | ComponentGeneralHeader | ComponentGeneralHeaderLink | ComponentMenuMenuItem | ComponentMenuMenuLink | ComponentMenuMenuSection | ComponentOsItemsAdvancedAccordionDepartment | ComponentOsItemsAdvancedAccordionItem | ComponentOsItemsAdvancedAccordionSubItem | ComponentOsItemsAdvancedAccordionSubSubItem | ComponentSectionsAccordion | ComponentSectionsBanner | ComponentSectionsBlogPostsByCategory | ComponentSectionsBlogPostsByTags | ComponentSectionsBlogPostsList | ComponentSectionsCalculator | ComponentSectionsColumnedText | ComponentSectionsComparisonSection | ComponentSectionsContactsSection | ComponentSectionsDivider | ComponentSectionsFaqCategories | ComponentSectionsFaqs | ComponentSectionsFeaturedBlogPosts | ComponentSectionsFileList | ComponentSectionsGallery | ComponentSectionsHomepageEvents | ComponentSectionsHomepageHighlights | ComponentSectionsHomepageMayorAndCouncil | ComponentSectionsHomepageTabs | ComponentSectionsIconTitleDesc | ComponentSectionsIframe | ComponentSectionsInbaArticlesList | ComponentSectionsInbaReleases | ComponentSectionsLinks | ComponentSectionsNarrowText | ComponentSectionsNumericalList | ComponentSectionsOfficialBoard | ComponentSectionsOrganizationalStructure | ComponentSectionsProsAndConsSection | ComponentSectionsRegulations | ComponentSectionsRegulationsList | ComponentSectionsSpace | ComponentSectionsSubpageList | ComponentSectionsTestimonials | ComponentSectionsTextWithImage | ComponentSectionsTimeline | ComponentSectionsTopServices | ComponentSectionsVideos | ComponentSectionsWaves | ComponentTaxAdministratorsTaxAdministrator | Faq | FaqCategory | Footer | General | Homepage | I18NLocale | InbaArticle | InbaRelease | InbaTag | Menu | Page | PageCategory | PageSubcategory | Regulation | Tag | TaxAdministratorsList | UploadFile | UploadFolder | UsersPermissionsPermission | UsersPermissionsRole | UsersPermissionsUser | Vzn type Homepage { bookmarkTourists: ComponentBlocksHomepageBookmark @@ -2896,6 +3044,10 @@ type Mutation { createAlertLocalization(data: AlertInput, id: ID, locale: I18NLocaleCode): AlertEntityResponse createBlogPost(data: BlogPostInput!, locale: I18NLocaleCode): BlogPostEntityResponse createBlogPostLocalization(data: BlogPostInput, id: ID, locale: I18NLocaleCode): BlogPostEntityResponse + createFaq(data: FaqInput!, locale: I18NLocaleCode): FaqEntityResponse + createFaqCategory(data: FaqCategoryInput!, locale: I18NLocaleCode): FaqCategoryEntityResponse + createFaqCategoryLocalization(data: FaqCategoryInput, id: ID, locale: I18NLocaleCode): FaqCategoryEntityResponse + createFaqLocalization(data: FaqInput, id: ID, locale: I18NLocaleCode): FaqEntityResponse createFooterLocalization(data: FooterInput, id: ID, locale: I18NLocaleCode): FooterEntityResponse createGeneralLocalization(data: GeneralInput, id: ID, locale: I18NLocaleCode): GeneralEntityResponse createHomepageLocalization(data: HomepageInput, id: ID, locale: I18NLocaleCode): HomepageEntityResponse @@ -2925,6 +3077,8 @@ type Mutation { createVzn(data: VznInput!): VznEntityResponse deleteAlert(locale: I18NLocaleCode): AlertEntityResponse deleteBlogPost(id: ID!, locale: I18NLocaleCode): BlogPostEntityResponse + deleteFaq(id: ID!, locale: I18NLocaleCode): FaqEntityResponse + deleteFaqCategory(id: ID!, locale: I18NLocaleCode): FaqCategoryEntityResponse deleteFooter(locale: I18NLocaleCode): FooterEntityResponse deleteGeneral(locale: I18NLocaleCode): GeneralEntityResponse deleteHomepage(locale: I18NLocaleCode): HomepageEntityResponse @@ -2966,6 +3120,8 @@ type Mutation { resetPassword(code: String!, password: String!, passwordConfirmation: String!): UsersPermissionsLoginPayload updateAlert(data: AlertInput!, locale: I18NLocaleCode): AlertEntityResponse updateBlogPost(data: BlogPostInput!, id: ID!, locale: I18NLocaleCode): BlogPostEntityResponse + updateFaq(data: FaqInput!, id: ID!, locale: I18NLocaleCode): FaqEntityResponse + updateFaqCategory(data: FaqCategoryInput!, id: ID!, locale: I18NLocaleCode): FaqCategoryEntityResponse updateFileInfo(id: ID!, info: FileInfoInput): UploadFileEntityResponse! updateFooter(data: FooterInput!, locale: I18NLocaleCode): FooterEntityResponse updateGeneral(data: GeneralInput!, locale: I18NLocaleCode): GeneralEntityResponse @@ -3137,7 +3293,7 @@ type PageRelationResponseCollection { data: [PageEntity!]! } -union PageSectionsDynamicZone = ComponentSectionsAccordion | ComponentSectionsBanner | ComponentSectionsBlogPostsByCategory | ComponentSectionsBlogPostsByTags | ComponentSectionsBlogPostsList | ComponentSectionsCalculator | ComponentSectionsColumnedText | ComponentSectionsComparisonSection | ComponentSectionsContactsSection | ComponentSectionsDivider | ComponentSectionsFeaturedBlogPosts | ComponentSectionsFileList | ComponentSectionsGallery | ComponentSectionsIconTitleDesc | ComponentSectionsIframe | ComponentSectionsInbaArticlesList | ComponentSectionsInbaReleases | ComponentSectionsLinks | ComponentSectionsNarrowText | ComponentSectionsNumericalList | ComponentSectionsOfficialBoard | ComponentSectionsOrganizationalStructure | ComponentSectionsProsAndConsSection | ComponentSectionsRegulations | ComponentSectionsRegulationsList | ComponentSectionsSpace | ComponentSectionsTestimonials | ComponentSectionsTextWithImage | ComponentSectionsTimeline | ComponentSectionsVideos | ComponentSectionsWaves | Error +union PageSectionsDynamicZone = ComponentSectionsAccordion | ComponentSectionsBanner | ComponentSectionsBlogPostsByCategory | ComponentSectionsBlogPostsByTags | ComponentSectionsBlogPostsList | ComponentSectionsCalculator | ComponentSectionsColumnedText | ComponentSectionsComparisonSection | ComponentSectionsContactsSection | ComponentSectionsDivider | ComponentSectionsFaqCategories | ComponentSectionsFaqs | ComponentSectionsFeaturedBlogPosts | ComponentSectionsFileList | ComponentSectionsGallery | ComponentSectionsIconTitleDesc | ComponentSectionsIframe | ComponentSectionsInbaArticlesList | ComponentSectionsInbaReleases | ComponentSectionsLinks | ComponentSectionsNarrowText | ComponentSectionsNumericalList | ComponentSectionsOfficialBoard | ComponentSectionsOrganizationalStructure | ComponentSectionsProsAndConsSection | ComponentSectionsRegulations | ComponentSectionsRegulationsList | ComponentSectionsSpace | ComponentSectionsTestimonials | ComponentSectionsTextWithImage | ComponentSectionsTimeline | ComponentSectionsVideos | ComponentSectionsWaves | Error scalar PageSectionsDynamicZoneInput @@ -3218,6 +3374,10 @@ type Query { alert(locale: I18NLocaleCode): AlertEntityResponse blogPost(id: ID, locale: I18NLocaleCode): BlogPostEntityResponse blogPosts(filters: BlogPostFiltersInput, locale: I18NLocaleCode, pagination: PaginationArg = {}, publicationState: PublicationState = LIVE, sort: [String] = []): BlogPostEntityResponseCollection + faq(id: ID, locale: I18NLocaleCode): FaqEntityResponse + faqCategories(filters: FaqCategoryFiltersInput, locale: I18NLocaleCode, pagination: PaginationArg = {}, publicationState: PublicationState = LIVE, sort: [String] = []): FaqCategoryEntityResponseCollection + faqCategory(id: ID, locale: I18NLocaleCode): FaqCategoryEntityResponse + faqs(filters: FaqFiltersInput, locale: I18NLocaleCode, pagination: PaginationArg = {}, publicationState: PublicationState = LIVE, sort: [String] = []): FaqEntityResponseCollection footer(locale: I18NLocaleCode, publicationState: PublicationState = LIVE): FooterEntityResponse general(locale: I18NLocaleCode): GeneralEntityResponse homepage(locale: I18NLocaleCode, publicationState: PublicationState = LIVE): HomepageEntityResponse diff --git a/strapi/src/api/faq-category/content-types/faq-category/schema.json b/strapi/src/api/faq-category/content-types/faq-category/schema.json new file mode 100644 index 000000000..619c14a80 --- /dev/null +++ b/strapi/src/api/faq-category/content-types/faq-category/schema.json @@ -0,0 +1,44 @@ +{ + "kind": "collectionType", + "collectionName": "faq_categories", + "info": { + "singularName": "faq-category", + "pluralName": "faq-categories", + "displayName": "faq category" + }, + "options": { + "draftAndPublish": true + }, + "pluginOptions": { + "i18n": { + "localized": true + } + }, + "attributes": { + "title": { + "pluginOptions": { + "i18n": { + "localized": true + } + }, + "type": "string", + "required": true + }, + "slug": { + "pluginOptions": { + "i18n": { + "localized": true + } + }, + "type": "uid", + "targetField": "title", + "required": true + }, + "faqs": { + "type": "relation", + "relation": "oneToMany", + "target": "api::faq.faq", + "mappedBy": "faqCategory" + } + } +} diff --git a/strapi/src/api/faq-category/controllers/faq-category.ts b/strapi/src/api/faq-category/controllers/faq-category.ts new file mode 100644 index 000000000..b565fcd56 --- /dev/null +++ b/strapi/src/api/faq-category/controllers/faq-category.ts @@ -0,0 +1,7 @@ +/** + * faq-category controller + */ + +import { factories } from '@strapi/strapi' + +export default factories.createCoreController('api::faq-category.faq-category'); diff --git a/strapi/src/api/faq-category/routes/faq-category.ts b/strapi/src/api/faq-category/routes/faq-category.ts new file mode 100644 index 000000000..eb55fd466 --- /dev/null +++ b/strapi/src/api/faq-category/routes/faq-category.ts @@ -0,0 +1,7 @@ +/** + * faq-category router + */ + +import { factories } from '@strapi/strapi'; + +export default factories.createCoreRouter('api::faq-category.faq-category'); diff --git a/strapi/src/api/faq-category/services/faq-category.ts b/strapi/src/api/faq-category/services/faq-category.ts new file mode 100644 index 000000000..1f05cbe70 --- /dev/null +++ b/strapi/src/api/faq-category/services/faq-category.ts @@ -0,0 +1,7 @@ +/** + * faq-category service + */ + +import { factories } from '@strapi/strapi'; + +export default factories.createCoreService('api::faq-category.faq-category'); diff --git a/strapi/src/api/faq/content-types/faq/schema.json b/strapi/src/api/faq/content-types/faq/schema.json new file mode 100644 index 000000000..d578493a5 --- /dev/null +++ b/strapi/src/api/faq/content-types/faq/schema.json @@ -0,0 +1,43 @@ +{ + "kind": "collectionType", + "collectionName": "faqs", + "info": { + "singularName": "faq", + "pluralName": "faqs", + "displayName": "faq" + }, + "options": { + "draftAndPublish": true + }, + "pluginOptions": { + "i18n": { + "localized": true + } + }, + "attributes": { + "title": { + "pluginOptions": { + "i18n": { + "localized": true + } + }, + "type": "string", + "required": true + }, + "body": { + "pluginOptions": { + "i18n": { + "localized": true + } + }, + "type": "richtext", + "required": false + }, + "faqCategory": { + "type": "relation", + "relation": "manyToOne", + "target": "api::faq-category.faq-category", + "inversedBy": "faqs" + } + } +} diff --git a/strapi/src/api/faq/controllers/faq.ts b/strapi/src/api/faq/controllers/faq.ts new file mode 100644 index 000000000..fa1905038 --- /dev/null +++ b/strapi/src/api/faq/controllers/faq.ts @@ -0,0 +1,7 @@ +/** + * faq controller + */ + +import { factories } from '@strapi/strapi' + +export default factories.createCoreController('api::faq.faq'); diff --git a/strapi/src/api/faq/routes/faq.ts b/strapi/src/api/faq/routes/faq.ts new file mode 100644 index 000000000..e9f783cb2 --- /dev/null +++ b/strapi/src/api/faq/routes/faq.ts @@ -0,0 +1,7 @@ +/** + * faq router + */ + +import { factories } from '@strapi/strapi'; + +export default factories.createCoreRouter('api::faq.faq'); diff --git a/strapi/src/api/faq/services/faq.ts b/strapi/src/api/faq/services/faq.ts new file mode 100644 index 000000000..5578d01fb --- /dev/null +++ b/strapi/src/api/faq/services/faq.ts @@ -0,0 +1,7 @@ +/** + * faq service + */ + +import { factories } from '@strapi/strapi'; + +export default factories.createCoreService('api::faq.faq'); diff --git a/strapi/src/api/page/content-types/page/schema.json b/strapi/src/api/page/content-types/page/schema.json index dea01616a..4bba674a9 100644 --- a/strapi/src/api/page/content-types/page/schema.json +++ b/strapi/src/api/page/content-types/page/schema.json @@ -119,6 +119,8 @@ "sections.comparison-section", "sections.contacts-section", "sections.divider", + "sections.faqs", + "sections.faq-categories", "sections.featured-blog-posts", "sections.file-list", "sections.gallery", diff --git a/strapi/src/components/sections/faq-categories.json b/strapi/src/components/sections/faq-categories.json new file mode 100644 index 000000000..8634ed60d --- /dev/null +++ b/strapi/src/components/sections/faq-categories.json @@ -0,0 +1,20 @@ +{ + "collectionName": "components_sections_faq_categories", + "info": { + "displayName": "faq categories" + }, + "options": {}, + "attributes": { + "title": { + "type": "string" + }, + "text": { + "type": "text" + }, + "faqCategories": { + "type": "relation", + "relation": "oneToMany", + "target": "api::faq-category.faq-category" + } + } +} diff --git a/strapi/src/components/sections/faqs.json b/strapi/src/components/sections/faqs.json new file mode 100644 index 000000000..66d122ae1 --- /dev/null +++ b/strapi/src/components/sections/faqs.json @@ -0,0 +1,21 @@ +{ + "collectionName": "components_sections_faqs", + "info": { + "displayName": "faqs", + "description": "" + }, + "options": {}, + "attributes": { + "title": { + "type": "string" + }, + "text": { + "type": "text" + }, + "faqs": { + "type": "relation", + "relation": "oneToMany", + "target": "api::faq.faq" + } + } +}