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 - top level navigation links #16793

Merged
merged 7 commits into from
Nov 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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[]
}
RunarVestmann marked this conversation as resolved.
Show resolved Hide resolved

@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
}`,
})
}
}

RunarVestmann marked this conversation as resolved.
Show resolved Hide resolved
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[]
}
Loading