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): Team List - Move frontend logic to the backend #15965

Merged
merged 4 commits into from
Sep 12, 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
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import { useMemo, useState } from 'react'
import { useState } from 'react'
import flatten from 'lodash/flatten'
import { useLazyQuery } from '@apollo/client'

import { TeamList, type TeamListProps } from '@island.is/island-ui/contentful'
import { sortAlpha } from '@island.is/shared/utils'
import { GenericList } from '@island.is/web/components'
import {
type GenericTag,
Expand Down Expand Up @@ -67,43 +66,7 @@ export const TeamMemberListWrapper = ({

const totalItems = itemsResponse?.total ?? 0

const items = useMemo(
() =>
(itemsResponse?.items ?? []).map((item) => {
const tagGroups: { groupLabel: string; tagLabels: string[] }[] = []
for (const tag of item.filterTags ?? []) {
if (!tag.genericTagGroup?.title || !tag.title) {
continue
}
const index = tagGroups.findIndex(
(group) => group.groupLabel === tag.genericTagGroup?.title,
)
if (index >= 0) {
tagGroups[index].tagLabels.push(tag.title)
} else {
tagGroups.push({
groupLabel: tag.genericTagGroup.title,
tagLabels: [tag.title],
})
}

// Add a colon to the end of group labels if it doesn't have one
for (const group of tagGroups) {
if (!group.groupLabel.endsWith(':')) {
group.groupLabel += ':'
}
}
}

tagGroups.sort(sortAlpha('groupLabel'))

return {
...(item as TeamListProps['teamMembers'][number]),
tagGroups,
}
}),
[itemsResponse],
)
const items = itemsResponse?.items ?? []

return (
<GenericList
Expand Down Expand Up @@ -132,7 +95,7 @@ export const TeamMemberListWrapper = ({
tagQueryId={tagQueryId}
>
<TeamList
teamMembers={items}
teamMembers={items as TeamListProps['teamMembers']}
variant="accordion"
RunarVestmann marked this conversation as resolved.
Show resolved Hide resolved
prefixes={{
email: activeLocale === 'is' ? 'Netfang:' : 'Email:',
Expand Down
12 changes: 3 additions & 9 deletions apps/web/screens/queries/TeamList.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,9 @@ export const GET_TEAM_MEMBERS_QUERY = gql`
title
email
phone
filterTags {
id
title
slug
genericTagGroup {
id
title
slug
}
tagGroups {
groupLabel
tagLabels
}
image {
...ImageFields
Expand Down
3 changes: 3 additions & 0 deletions libs/cms/src/lib/generated/contentfulTypes.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4369,6 +4369,9 @@ export interface ITeamListFields {
/** Filter Tags */
filterTags?: IGenericTag[] | undefined

/** Filter groups */
filterGroups?: IGenericTagGroup[] | undefined

/** Variant */
variant?: 'card' | 'accordion' | undefined
}
Expand Down
48 changes: 40 additions & 8 deletions libs/cms/src/lib/models/teamList.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { CacheField } from '@island.is/nest/graphql'
import { ITeamList } from '../generated/contentfulTypes'
import { SystemMetadata } from '@island.is/shared/types'
import { TeamMember, mapTeamMember } from './teamMember.model'
import { GenericTag, mapGenericTag } from './genericTag.model'
import { GenericTag } from './genericTag.model'

@ObjectType()
export class TeamList {
Expand All @@ -23,10 +23,42 @@ export class TeamList {
export const mapTeamList = ({
fields,
sys,
}: ITeamList): SystemMetadata<TeamList> => ({
typename: 'TeamList',
id: sys.id,
teamMembers: (fields.teamMembers ?? []).map(mapTeamMember),
variant: fields.variant === 'accordion' ? 'accordion' : 'card',
filterTags: fields.filterTags ? fields.filterTags.map(mapGenericTag) : [],
})
}: ITeamList): SystemMetadata<TeamList> => {
const teamMembers = (fields.teamMembers ?? []).map(mapTeamMember)

const filterTags: GenericTag[] = []

for (const teamMember of teamMembers) {
for (const tag of teamMember.filterTags ?? []) {
const tagBelongsToAFilterGroup = fields.filterGroups?.some(
(group) => group?.sys?.id === tag?.genericTagGroup?.id,
)
if (tag?.genericTagGroup?.id && tagBelongsToAFilterGroup) {
const tagHasAlreadyBeenAdded = filterTags.some((t) => t.id === tag.id)
if (!tagHasAlreadyBeenAdded) {
filterTags.push(tag)
}
}
}

// Reorder the team member tag groups so they are in the same order as the team list filter groups
const tagGroups = []
for (const filterGroup of fields.filterGroups ?? []) {
const group = teamMember.tagGroups?.find(
(g) => g.groupId === filterGroup.sys.id,
)
if (group) {
tagGroups.push(group)
}
}
teamMember.tagGroups = tagGroups
}

return {
typename: 'TeamList',
id: sys.id,
teamMembers,
variant: fields.variant === 'accordion' ? 'accordion' : 'card',
filterTags,
}
}
79 changes: 68 additions & 11 deletions libs/cms/src/lib/models/teamMember.model.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,27 @@
import { Field, ObjectType } from '@nestjs/graphql'
import { Field, ObjectType, ID } from '@nestjs/graphql'
import { CacheField } from '@island.is/nest/graphql'
import { ITeamMember } from '../generated/contentfulTypes'
import { Image, mapImage } from './image.model'
import { GenericTag, mapGenericTag } from './genericTag.model'
import { SliceUnion, mapDocument } from '../unions/slice.union'

@ObjectType()
export class TeamMemberTagGroup {
@Field()
groupId!: string

@Field()
groupLabel!: string

@Field(() => [String])
tagLabels!: string[]
}

@ObjectType()
export class TeamMember {
@Field(() => ID)
id?: string

@Field()
name!: string

Expand All @@ -30,15 +45,57 @@ export class TeamMember {

@CacheField(() => [SliceUnion], { nullable: true })
intro?: Array<typeof SliceUnion> = []

@CacheField(() => [TeamMemberTagGroup], { nullable: true })
tagGroups?: TeamMemberTagGroup[]

@Field({ nullable: true })
createdAt?: string
}

export const mapTeamMember = ({ fields, sys }: ITeamMember): TeamMember => ({
name: fields.name ?? '',
title: fields.title ?? '',
image: mapImage(fields.mynd),
imageOnSelect: fields.imageOnSelect ? mapImage(fields.imageOnSelect) : null,
filterTags: fields.filterTags ? fields.filterTags.map(mapGenericTag) : [],
intro: fields.intro ? mapDocument(fields.intro, `${sys.id}:intro`) : [],
email: fields.email,
phone: fields.phone,
})
export const mapTeamMember = ({ fields, sys }: ITeamMember): TeamMember => {
const tagGroups: TeamMemberTagGroup[] = []

const filterTags = fields.filterTags
? fields.filterTags.map(mapGenericTag)
: []

for (const tag of filterTags) {
if (!tag.genericTagGroup?.title || !tag.title) {
continue
}
const index = tagGroups.findIndex(
(group) => group.groupLabel === tag.genericTagGroup?.title,
)
if (index >= 0) {
tagGroups[index].tagLabels.push(tag.title)
} else {
tagGroups.push({
groupLabel: tag.genericTagGroup.title,
tagLabels: [tag.title],
groupId: tag.genericTagGroup.id,
})
}

// Add a colon to the end of group labels if it doesn't have one
for (const group of tagGroups) {
if (!group.groupLabel.endsWith(':')) {
group.groupLabel += ':'
}
}
}

return {
id: sys.id,
name: fields.name ?? '',
title: fields.title ?? '',
image: mapImage(fields.mynd),
imageOnSelect: fields.imageOnSelect ? mapImage(fields.imageOnSelect) : null,
filterTags,
intro: fields.intro ? mapDocument(fields.intro, `${sys.id}:intro`) : [],
email: fields.email,
phone: fields.phone,
tagGroups,
createdAt: sys.createdAt,
}
}
28 changes: 16 additions & 12 deletions libs/cms/src/lib/search/importers/teamList.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { logger } from '@island.is/logging'
import { Injectable } from '@nestjs/common'
import type { ITeamList } from '../../generated/contentfulTypes'
import { CmsSyncProvider, processSyncDataInput } from '../cmsSync.service'
import { mapTeamMember } from '../../models/teamMember.model'
import { mapTeamList } from '../../models/teamList.model'

@Injectable()
export class TeamListSyncService implements CmsSyncProvider<ITeamList> {
Expand All @@ -21,30 +21,34 @@ export class TeamListSyncService implements CmsSyncProvider<ITeamList> {
const teamMembers: MappedData[] = []

for (const teamListEntry of entries) {
for (const teamMemberEntry of teamListEntry.fields.teamMembers ?? []) {
const teamList = mapTeamList(teamListEntry)
for (const member of teamList.teamMembers ?? []) {
try {
const content = teamMemberEntry.fields.name
? teamMemberEntry.fields.name
: undefined

const content = member.name ? member.name : undefined
teamMembers.push({
_id: teamMemberEntry.sys.id,
title: teamMemberEntry.fields.name,
_id: member.id,
title: member.name,
content,
contentWordCount: content?.split(/\s+/).length,
type: 'webTeamMember',
response: JSON.stringify({
...mapTeamMember(teamMemberEntry),
...member,
typename: 'webTeamMember',
}),
tags: [{ key: teamListEntry.sys.id, type: 'referencedBy' }],
dateCreated: teamMemberEntry.sys.createdAt,
tags: [
{ key: teamListEntry.sys.id, type: 'referencedBy' },
...(member.filterTags ?? []).map((tag) => ({
key: tag.slug,
type: 'genericTag',
})),
],
dateCreated: member.createdAt ?? '',
dateUpdated: new Date().getTime().toString(),
})
} catch (error) {
logger.warn('Failed to import Team Member', {
error: error.message,
id: teamMemberEntry?.sys?.id,
id: member?.id,
})
}
}
Expand Down
4 changes: 2 additions & 2 deletions libs/island-ui/contentful/src/lib/TeamList/TeamList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -144,8 +144,8 @@ const TeamMemberAccordionList = ({
}: Pick<TeamListProps, 'teamMembers' | 'prefixes'>) => {
return (
<Accordion singleExpand={false}>
{teamMembers.map((member) => {
const id = `${member.name}-${member.title}`
{teamMembers.map((member, index) => {
const id = `${member.name}-${member.title}-${index}`
return (
<AccordionItem
key={id}
Expand Down
Loading