Skip to content

Commit

Permalink
feat(web): Organization page - top level navigation links (#16793)
Browse files Browse the repository at this point in the history
* Add top level navigation field to organization page model

* Add missing slash prefix

* Remove sitemap model

* Fix export

---------

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
  • Loading branch information
RunarVestmann and kodiakhq[bot] authored Nov 13, 2024
1 parent 171a0a6 commit 0e05570
Show file tree
Hide file tree
Showing 5 changed files with 191 additions and 61 deletions.
37 changes: 7 additions & 30 deletions apps/contentful-apps/components/sitemap/utils.ts
Original file line number Diff line number Diff line change
@@ -1,37 +1,14 @@
import { FieldExtensionSDK } from '@contentful/app-sdk'

export enum TreeNodeType {
ENTRY = 'entry',
CATEGORY = 'category',
URL = 'url',
}
import {
SitemapTree as Tree,
SitemapTreeNode as TreeNode,
SitemapTreeNodeType as TreeNodeType,
} from '@island.is/shared/types'

export const ENTRY_CONTENT_TYPE_ID = 'organizationParentSubpage'
export { type Tree, type TreeNode, TreeNodeType }

export type TreeNode = Tree &
(
| {
type: TreeNodeType.ENTRY
entryId: string
primaryLocation: boolean // Whether the parent nodes above are the main breadcrumb path (always true unless the entry is in multiple places in the sitemap)
}
| {
type: TreeNodeType.CATEGORY
label: string
slug: string
description: string
}
| {
type: TreeNodeType.URL
label: string
url: string
}
)

export type Tree = {
id: number
childNodes: TreeNode[]
}
export const ENTRY_CONTENT_TYPE_ID = 'organizationParentSubpage'

const getHighestId = (tree: Tree) => {
let highestId = tree.id
Expand Down
6 changes: 6 additions & 0 deletions apps/web/screens/queries/Organization.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,12 @@ export const GET_ORGANIZATION_PAGE_QUERY = gql`
slug
title
description
topLevelNavigation {
links {
label
href
}
}
defaultHeaderImage {
url
contentType
Expand Down
72 changes: 72 additions & 0 deletions libs/cms/src/lib/generated/contentfulTypes.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3066,9 +3066,14 @@ export interface IOrganizationPageFields {
| 'vinnueftirlitid'
| 'hljodbokasafn-islands'
| 'thjodskjalasafn'
| 'faggilding'
| 'standalone'

/** Theme Properties */
themeProperties?: Record<string, any> | undefined

/** Sitemap */
sitemap?: ISitemap | undefined
}

export interface IOrganizationPage extends Entry<IOrganizationPageFields> {
Expand All @@ -3088,6 +3093,46 @@ export interface IOrganizationPage extends Entry<IOrganizationPageFields> {
}
}

export interface IOrganizationParentSubpageFields {
/** Internal Title */
internalTitle: string

/** Displayed Title */
title: string

/** Slug */
slug?: string | undefined

/** Pages */
pages: IOrganizationSubpage[]

/** Related Content */
relatedContent?: ILink[] | undefined

/** Image */
image?: Asset | undefined
}

/** Navigation page for content that belongs in multiple organization subpages */

export interface IOrganizationParentSubpage
extends Entry<IOrganizationParentSubpageFields> {
sys: {
id: string
type: string
createdAt: string
updatedAt: string
locale: string
contentType: {
sys: {
id: 'organizationParentSubpage'
linkType: 'ContentType'
type: 'Link'
}
}
}
}

export interface IOrganizationSubpageFields {
/** Organization Page */
organizationPage: IOrganizationPage
Expand Down Expand Up @@ -3771,6 +3816,31 @@ export interface ISidebarCard extends Entry<ISidebarCardFields> {
}
}

export interface ISitemapFields {
/** Title */
title?: string | undefined

/** Tree */
tree?: Record<string, any> | undefined
}

export interface ISitemap extends Entry<ISitemapFields> {
sys: {
id: string
type: string
createdAt: string
updatedAt: string
locale: string
contentType: {
sys: {
id: 'sitemap'
linkType: 'ContentType'
type: 'Link'
}
}
}
}

export interface ISliceConnectedComponentFields {
/** Title */
title: string
Expand Down Expand Up @@ -5100,6 +5170,7 @@ export type CONTENT_TYPE =
| 'openDataSubpage'
| 'organization'
| 'organizationPage'
| 'organizationParentSubpage'
| 'organizationSubpage'
| 'organizationTag'
| 'overviewLinks'
Expand All @@ -5115,6 +5186,7 @@ export type CONTENT_TYPE =
| 'sectionWithVideo'
| 'serviceWebPage'
| 'sidebarCard'
| 'sitemap'
| 'sliceConnectedComponent'
| 'sliceDropdown'
| 'statistic'
Expand Down
106 changes: 75 additions & 31 deletions libs/cms/src/lib/models/organizationPage.model.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { Field, ObjectType, ID } from '@nestjs/graphql'
import { CacheField } from '@island.is/nest/graphql'
import { SitemapTree, SitemapTreeNodeType } from '@island.is/shared/types'
import { getOrganizationPageUrlPrefix } from '@island.is/shared/utils'

import { IOrganizationPage } from '../generated/contentfulTypes'
import { mapOrganization, Organization } from './organization.model'
Expand All @@ -14,6 +16,21 @@ import {
import { GenericTag, mapGenericTag } from './genericTag.model'
import { AlertBanner, mapAlertBanner } from './alertBanner.model'

@ObjectType()
class OrganizationPageTopLevelNavigationLink {
@Field()
label!: string

@Field()
href!: string
}

@ObjectType()
class OrganizationPageTopLevelNavigation {
@CacheField(() => [OrganizationPageTopLevelNavigationLink])
links!: OrganizationPageTopLevelNavigationLink[]
}

@ObjectType()
export class OrganizationPage {
@Field(() => ID)
Expand Down Expand Up @@ -66,39 +83,66 @@ export class OrganizationPage {

@CacheField(() => Image, { nullable: true })
defaultHeaderImage?: Image

@CacheField(() => OrganizationPageTopLevelNavigation, { nullable: true })
topLevelNavigation?: OrganizationPageTopLevelNavigation | null
}

export const mapOrganizationPage = ({
sys,
fields,
}: IOrganizationPage): OrganizationPage => ({
id: sys.id,
title: fields.title ?? '',
slug: ((fields.slug || fields.organization?.fields?.slug) ?? '').trim(),
description: fields.description ?? '',
theme: fields.theme ?? 'default',
themeProperties: mapOrganizationTheme(fields.themeProperties ?? {}),
slices: (fields.slices ?? []).map(safelyMapSliceUnion).filter(Boolean),
bottomSlices: (fields.bottomSlices ?? [])
.map(safelyMapSliceUnion)
.filter(Boolean),
secondaryNewsTags: (fields.secondaryNewsTags ?? []).map(mapGenericTag),
menuLinks: (fields.menuLinks ?? []).map(mapLinkGroup),
secondaryMenu: fields.secondaryMenu
? mapLinkGroup(fields.secondaryMenu)
: null,
organization: fields.organization
? mapOrganization(fields.organization)
: null,
featuredImage: fields.featuredImage ? mapImage(fields.featuredImage) : null,
sidebarCards: (fields.sidebarCards ?? [])
.map(safelyMapSliceUnion)
.filter(Boolean),
externalLinks: (fields.externalLinks ?? []).map(mapLink),
alertBanner: fields.alertBanner
? mapAlertBanner(fields.alertBanner)
: undefined,
defaultHeaderImage: fields.defaultHeaderImage
? mapImage(fields.defaultHeaderImage)
: undefined,
})
}: IOrganizationPage): OrganizationPage => {
const slug = ((fields.slug || fields.organization?.fields?.slug) ?? '').trim()

const topLevelNavigation: OrganizationPageTopLevelNavigation = { links: [] }

// Extract top level navigation from sitemap tree
for (const node of (fields.sitemap?.fields?.tree as SitemapTree)
?.childNodes ?? []) {
if (
node.type === SitemapTreeNodeType.CATEGORY &&
Boolean(node.label) &&
Boolean(node.slug)
) {
topLevelNavigation.links.push({
label: node.label,
href: `/${getOrganizationPageUrlPrefix(sys.locale)}/${slug}/${
node.slug
}`,
})
}
}

return {
id: sys.id,
title: fields.title ?? '',
slug,
description: fields.description ?? '',
theme: fields.theme ?? 'default',
themeProperties: mapOrganizationTheme(fields.themeProperties ?? {}),
slices: (fields.slices ?? []).map(safelyMapSliceUnion).filter(Boolean),
bottomSlices: (fields.bottomSlices ?? [])
.map(safelyMapSliceUnion)
.filter(Boolean),
secondaryNewsTags: (fields.secondaryNewsTags ?? []).map(mapGenericTag),
menuLinks: (fields.menuLinks ?? []).map(mapLinkGroup),
secondaryMenu: fields.secondaryMenu
? mapLinkGroup(fields.secondaryMenu)
: null,
organization: fields.organization
? mapOrganization(fields.organization)
: null,
featuredImage: fields.featuredImage ? mapImage(fields.featuredImage) : null,
sidebarCards: (fields.sidebarCards ?? [])
.map(safelyMapSliceUnion)
.filter(Boolean),
externalLinks: (fields.externalLinks ?? []).map(mapLink),
alertBanner: fields.alertBanner
? mapAlertBanner(fields.alertBanner)
: undefined,
defaultHeaderImage: fields.defaultHeaderImage
? mapImage(fields.defaultHeaderImage)
: undefined,
topLevelNavigation,
}
}
31 changes: 31 additions & 0 deletions libs/shared/types/src/lib/api-cms-domain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,34 @@ export interface StatisticSourceValue {
export type StatisticSourceData<T extends string = string> = {
data: Record<T, StatisticSourceValue[]>
}

export enum SitemapTreeNodeType {
ENTRY = 'entry',
CATEGORY = 'category',
URL = 'url',
}

export type SitemapTreeNode = SitemapTree &
(
| {
type: SitemapTreeNodeType.ENTRY
entryId: string
primaryLocation: boolean // Whether the parent nodes above are the "main breadcrumb path" (always true unless the entry is in multiple places in the sitemap)
}
| {
type: SitemapTreeNodeType.CATEGORY
label: string
slug: string
description: string
}
| {
type: SitemapTreeNodeType.URL
label: string
url: string
}
)

export type SitemapTree = {
id: number
childNodes: SitemapTreeNode[]
}

0 comments on commit 0e05570

Please sign in to comment.