Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(web): Organization page with "standalone" theme - Level 1 sitemap #17068

Merged
35 changes: 30 additions & 5 deletions apps/web/pages/s/[...slugs]/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { type FC } from 'react'

import {
import type {
Query,
QueryGetOrganizationPageArgs,
} from '@island.is/web/graphql/schema'
Expand Down Expand Up @@ -31,6 +31,9 @@ import PublishedMaterial, {
import StandaloneHome, {
type StandaloneHomeProps,
} from '@island.is/web/screens/Organization/Standalone/Home'
import StandaloneLevel1Sitemap, {
type StandaloneLevel1SitemapProps,
} from '@island.is/web/screens/Organization/Standalone/Level1Sitemap'
import StandaloneParentSubpage, {
StandaloneParentSubpageProps,
} from '@island.is/web/screens/Organization/Standalone/ParentSubpage'
Expand All @@ -46,6 +49,7 @@ enum PageType {
FRONTPAGE = 'frontpage',
STANDALONE_FRONTPAGE = 'standalone-frontpage',
STANDALONE_PARENT_SUBPAGE = 'standalone-parent-subpage',
STANDALONE_LEVEL1_SITEMAP = 'standalone-level1-sitemap',
SUBPAGE = 'subpage',
ALL_NEWS = 'news',
PUBLISHED_MATERIAL = 'published-material',
Expand All @@ -62,6 +66,9 @@ const pageMap: Record<PageType, FC<any>> = {
[PageType.STANDALONE_PARENT_SUBPAGE]: (props) => (
<StandaloneParentSubpage {...props} />
),
[PageType.STANDALONE_LEVEL1_SITEMAP]: (props) => (
<StandaloneLevel1Sitemap {...props} />
),
[PageType.SUBPAGE]: (props) => <SubPage {...props} />,
[PageType.ALL_NEWS]: (props) => <OrganizationNewsList {...props} />,
[PageType.PUBLISHED_MATERIAL]: (props) => <PublishedMaterial {...props} />,
Expand Down Expand Up @@ -90,6 +97,10 @@ interface Props {
type: PageType.STANDALONE_PARENT_SUBPAGE
props: StandaloneParentSubpageProps
}
| {
type: PageType.STANDALONE_LEVEL1_SITEMAP
props: StandaloneLevel1SitemapProps
}
| {
type: PageType.SUBPAGE
props: {
Expand Down Expand Up @@ -164,10 +175,10 @@ Component.getProps = async (context) => {

const modifiedContext = { ...context, organizationPage }

const STANDALONE_THEME = 'standalone'
const isStandaloneTheme = organizationPage.theme === 'standalone'

if (slugs.length === 1) {
if (organizationPage.theme === STANDALONE_THEME) {
if (isStandaloneTheme) {
return {
page: {
type: PageType.STANDALONE_FRONTPAGE,
Expand Down Expand Up @@ -237,7 +248,21 @@ Component.getProps = async (context) => {
}
}

if (organizationPage.theme === STANDALONE_THEME) {
if (
isStandaloneTheme &&
organizationPage.topLevelNavigation?.links.some(
(link) => slugs[1] === link.href.split('/').pop(),
)
) {
return {
page: {
type: PageType.STANDALONE_LEVEL1_SITEMAP,
props: await StandaloneLevel1Sitemap.getProps(modifiedContext),
},
}
}

if (isStandaloneTheme) {
return {
page: {
type: PageType.STANDALONE_PARENT_SUBPAGE,
Expand Down Expand Up @@ -291,7 +316,7 @@ Component.getProps = async (context) => {
}
}

if (organizationPage.theme === STANDALONE_THEME) {
if (isStandaloneTheme) {
return {
page: {
type: PageType.STANDALONE_PARENT_SUBPAGE,
Expand Down
154 changes: 154 additions & 0 deletions apps/web/screens/Organization/Standalone/Level1Sitemap.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
import {
CategoryCard,
GridColumn,
GridContainer,
GridRow,
Stack,
} from '@island.is/island-ui/core'
import { Text } from '@island.is/island-ui/core'
import type { SpanType } from '@island.is/island-ui/core/types'
import {
ContentLanguage,
OrganizationPage,
Query,
QueryGetOrganizationPageArgs,
QueryGetOrganizationPageStandaloneSitemapLevel1Args,
} from '@island.is/web/graphql/schema'
import { StandaloneLayout } from '@island.is/web/layouts/organization/standalone'
import type { Screen, ScreenContext } from '@island.is/web/types'
import { CustomNextError } from '@island.is/web/units/errors'

import {
GET_ORGANIZATION_PAGE_QUERY,
GET_ORGANIZATION_PAGE_STANDALONE_SITEMAP_LEVEL1_QUERY,
} from '../../queries'

const GRID_COLUMN_SPAN: SpanType = ['1/1', '1/1', '1/1', '1/2', '1/3']

type StandaloneLevel1SitemapScreenContext = ScreenContext & {
organizationPage?: Query['getOrganizationPage']
}

export interface StandaloneLevel1SitemapProps {
organizationPage: OrganizationPage
categoryTitle: string
items: {
label: string
href: string
description?: string | null
}[]
}

const StandaloneLevel1Sitemap: Screen<
StandaloneLevel1SitemapProps,
StandaloneLevel1SitemapScreenContext
> = ({ organizationPage, categoryTitle, items }) => {
return (
<StandaloneLayout
organizationPage={organizationPage}
seo={{
title: `${categoryTitle} | ${organizationPage.title}`,
}}
>
<GridContainer>
<GridRow>
<GridColumn span={['9/9', '9/9', '7/9']} offset={['0', '0', '1/9']}>
<Stack space={3}>
<Text variant="h1" as="h1">
{categoryTitle}
</Text>
<GridRow rowGap={3}>
{items.map(({ label, description, href }, index) => (
<GridColumn span={GRID_COLUMN_SPAN} key={index}>
<CategoryCard
heading={label}
headingAs="h2"
headingVariant="h3"
text={description ?? ''}
href={href}
/>
</GridColumn>
))}
RunarVestmann marked this conversation as resolved.
Show resolved Hide resolved
</GridRow>
</Stack>
</GridColumn>
</GridRow>
</GridContainer>
</StandaloneLayout>
)
}

StandaloneLevel1Sitemap.getProps = async ({
apolloClient,
locale,
query,
organizationPage,
}) => {
const [organizationPageSlug, categorySlug] = (query.slugs ?? []) as string[]

RunarVestmann marked this conversation as resolved.
Show resolved Hide resolved
const [
{
data: { getOrganizationPage },
},
{
data: { getOrganizationPageStandaloneSitemapLevel1 },
},
] = await Promise.all([
!organizationPage
? apolloClient.query<Query, QueryGetOrganizationPageArgs>({
query: GET_ORGANIZATION_PAGE_QUERY,
variables: {
input: {
slug: organizationPageSlug,
lang: locale as ContentLanguage,
},
},
})
: {
data: { getOrganizationPage: organizationPage },
},
apolloClient.query<
Query,
QueryGetOrganizationPageStandaloneSitemapLevel1Args
>({
query: GET_ORGANIZATION_PAGE_STANDALONE_SITEMAP_LEVEL1_QUERY,
variables: {
input: {
organizationPageSlug,
categorySlug,
lang: locale as ContentLanguage,
},
},
}),
])

if (!getOrganizationPage) {
throw new CustomNextError(404, 'Organization page was not found')
}

if (!getOrganizationPageStandaloneSitemapLevel1) {
throw new CustomNextError(
404,
'Organization page standalone level 1 sitemap was not found',
)
}

const categoryTitle = organizationPage?.topLevelNavigation?.links.find(
(link) => categorySlug === link.href.split('/').pop(),
)?.label

if (!categoryTitle) {
throw new CustomNextError(
404,
'Organization page standalone level 1 category was not found',
)
}

return {
organizationPage: getOrganizationPage,
categoryTitle,
items: getOrganizationPageStandaloneSitemapLevel1.childLinks,
}
}

export default StandaloneLevel1Sitemap
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,12 @@ const StandaloneParentSubpage: Screen<
const { linkResolver } = useLinkResolver()

return (
<StandaloneLayout organizationPage={organizationPage}>
<StandaloneLayout
organizationPage={organizationPage}
seo={{
title: `${subpage.title} | ${organizationPage.title}`,
}}
>
<GridContainer>
<GridRow>
<GridColumn span={['9/9', '9/9', '7/9']} offset={['0', '0', '1/9']}>
Expand Down
14 changes: 14 additions & 0 deletions apps/web/screens/queries/Organization.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -430,3 +430,17 @@ export const GET_ORGANIZATION_PARENT_SUBPAGE_QUERY = gql`
}
}
`

export const GET_ORGANIZATION_PAGE_STANDALONE_SITEMAP_LEVEL1_QUERY = gql`
query GetOrganizationPageStandaloneSitemapLevel1Query(
$input: GetOrganizationPageStandaloneSitemapLevel1Input!
) {
getOrganizationPageStandaloneSitemapLevel1(input: $input) {
childLinks {
label
href
description
}
}
}
`
93 changes: 93 additions & 0 deletions libs/cms/src/lib/cms.contentful.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,10 @@ import { mapServiceWebPage } from './models/serviceWebPage.model'
import { mapEvent } from './models/event.model'
import { GetOrganizationParentSubpageInput } from './dto/getOrganizationParentSubpage.input'
import { mapOrganizationParentSubpage } from './models/organizationParentSubpage.model'
import { GetOrganizationPageStandaloneSitemapLevel1Input } from './dto/getOrganizationPageStandaloneSitemap.input'
import { OrganizationPageStandaloneSitemap } from './models/organizationPageStandaloneSitemap.model'
import { SitemapTree, SitemapTreeNodeType } from '@island.is/shared/types'
import { getOrganizationPageUrlPrefix } from '@island.is/shared/utils'

const errorHandler = (name: string) => {
return (error: Error) => {
Expand Down Expand Up @@ -1175,4 +1179,93 @@ export class CmsContentfulService {
)[0] ?? null
)
}

async getOrganizationPageStandaloneSitemapLevel1(
input: GetOrganizationPageStandaloneSitemapLevel1Input,
): Promise<OrganizationPageStandaloneSitemap | null> {
const params = {
content_type: 'organizationPage',
'fields.slug': input.organizationPageSlug,
select: 'fields.sitemap',
limit: 1,
}

const response = await this.contentfulRepository
.getLocalizedEntries<types.IOrganizationPageFields>(input.lang, params)
.catch(errorHandler('getOrganizationPageStandaloneSitemapLevel1'))

const tree = response.items[0].fields.sitemap?.fields.tree as SitemapTree
RunarVestmann marked this conversation as resolved.
Show resolved Hide resolved

if (!tree) {
return null
}

const category = tree.childNodes.find(
(node) =>
node.type === SitemapTreeNodeType.CATEGORY &&
node.slug === input.categorySlug,
)

if (!category) {
return null
}

const entryNodes = new Map<string, { label: string; href: string }[]>()

const result = {
childLinks: category.childNodes.map((node) => {
if (node.type === SitemapTreeNodeType.CATEGORY) {
return {
label: node.label,
href: `/${getOrganizationPageUrlPrefix(input.lang)}/${
input.organizationPageSlug
}/${node.slug}`,
description: node.description,
}
}
if (node.type === SitemapTreeNodeType.URL) {
return {
label: node.label,
href: node.url,
}
}

// We need to fetch the label and href for all entry nodes, so we store them in a map
const entryNode = {
label: node.entryId,
href: node.entryId,
}
const nodeList = entryNodes.get(node.entryId) ?? []
nodeList.push(entryNode)
entryNodes.set(node.entryId, nodeList)
return entryNode
}),
}

const parentSubpageResponse =
await this.contentfulRepository.getLocalizedEntries<types.IOrganizationParentSubpageFields>(
input.lang,
{
content_type: 'organizationParentSubpage',
'sys.id[in]': Array.from(entryNodes.keys()).join(','),
limit: 1000,
},
1,
)

for (const parentSubpage of parentSubpageResponse.items) {
const nodeList = entryNodes.get(parentSubpage.sys.id)
if (!nodeList) {
continue
}
for (const node of nodeList) {
node.label = parentSubpage.fields.title
node.href = `/${getOrganizationPageUrlPrefix(input.lang)}/${
input.organizationPageSlug
}/${parentSubpage.fields.slug}`
}
}
RunarVestmann marked this conversation as resolved.
Show resolved Hide resolved

return result
}
}
Loading
Loading