Skip to content

Commit

Permalink
Merge branch 'main' into j-s/civil-demands
Browse files Browse the repository at this point in the history
  • Loading branch information
kodiakhq[bot] authored Sep 16, 2024
2 parents f9856d1 + c2ab2f5 commit 2041177
Show file tree
Hide file tree
Showing 93 changed files with 2,039 additions and 331 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ export const GenericListEditor = () => {
skip,
'fields.internalTitle[match]': searchValue,
'fields.genericList.sys.id': sdk.entry.getSys().id,
'sys.archivedAt[exists]': false,
},
})
if (
Expand Down
180 changes: 180 additions & 0 deletions apps/contentful-apps/pages/fields/generic-tag-group-items-field.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
import { useEffect, useRef, useState } from 'react'
import { useDebounce } from 'react-use'
import type { FieldExtensionSDK } from '@contentful/app-sdk'
import {
Box,
Button,
EntryCard,
Pagination,
Spinner,
Stack,
Text,
TextInput,
} from '@contentful/f36-components'
import { PlusIcon } from '@contentful/f36-icons'
import { useCMA, useSDK } from '@contentful/react-apps-toolkit'

const LIST_ITEMS_PER_PAGE = 4
const SEARCH_DEBOUNCE_TIME_IN_MS = 300

const GenericTagGroupItemsField = () => {
const [page, setPage] = useState(0)
const pageRef = useRef(0)
const [searchValue, setSearchValue] = useState('')
const searchValueRef = useRef('')
const [listItemResponse, setListItemResponse] = useState(null)
const [isLoading, setIsLoading] = useState(false)

const [counter, setCounter] = useState(0)

const sdk = useSDK<FieldExtensionSDK>()
const cma = useCMA()

const skip = LIST_ITEMS_PER_PAGE * page

const createGenericTag = async () => {
const tag = await cma.entry.create(
{
contentTypeId: 'genericTag',
environmentId: sdk.ids.environment,
spaceId: sdk.ids.space,
},
{
fields: {
genericTagGroup: {
[sdk.locales.default]: {
sys: {
id: sdk.entry.getSys().id,
linkType: 'Entry',
type: 'Link',
},
},
},
},
},
)
sdk.navigator
.openEntry(tag.sys.id, {
slideIn: { waitForClose: true },
})
.then(() => {
setCounter((c) => c + 1)
})
}

useDebounce(
async () => {
setIsLoading(true)
try {
const response = await cma.entry.getMany({
query: {
content_type: 'genericTag',
limit: LIST_ITEMS_PER_PAGE,
skip,
'fields.internalTitle[match]': searchValue,
'fields.genericTagGroup.sys.id': sdk.entry.getSys().id,
'sys.archivedAt[exists]': false,
},
})

if (
searchValueRef.current === searchValue &&
pageRef.current === page
) {
setListItemResponse(response)
}
} finally {
setIsLoading(false)
}
},
SEARCH_DEBOUNCE_TIME_IN_MS,
[page, searchValue, counter],
)

useEffect(() => {
sdk.window.startAutoResizer()
return () => {
sdk.window.stopAutoResizer()
}
}, [sdk.window])

return (
<div style={{ display: 'flex', flexDirection: 'column', gap: '24px' }}>
<Box>
<Box
onClick={createGenericTag}
style={{ display: 'flex', justifyContent: 'flex-end' }}
>
<Button startIcon={<PlusIcon />}>Create tag</Button>
</Box>
</Box>
<Box style={{ display: 'flex', flexFlow: 'column nowrap', gap: '24px' }}>
<TextInput
placeholder="Search for a generic tag"
value={searchValue}
onChange={(ev) => {
searchValueRef.current = ev.target.value
setSearchValue(ev.target.value)
setPage(0)
pageRef.current = 0
}}
/>

<Box
style={{
display: 'flex',
justifyContent: 'center',
visibility: isLoading ? 'visible' : 'hidden',
}}
>
<Spinner />
</Box>

{listItemResponse?.items?.length > 0 && (
<>
<Box style={{ minHeight: '440px' }}>
<Stack flexDirection="column" spacing="spacingL">
{listItemResponse.items.map((item) => (
<EntryCard
key={item.sys.id}
contentType="Generic Tag"
title={
item.fields.internalTitle?.[sdk.locales.default] ??
'Untitled'
}
onClick={() => {
sdk.navigator
.openEntry(item.sys.id, {
slideIn: { waitForClose: true },
})
.then(() => {
setCounter((c) => c + 1)
})
}}
/>
))}
</Stack>
</Box>
<Pagination
activePage={page}
itemsPerPage={LIST_ITEMS_PER_PAGE}
totalItems={listItemResponse.total}
onPageChange={(newPage) => {
pageRef.current = newPage
setPage(newPage)
}}
/>
</>
)}

{listItemResponse?.items?.length === 0 && (
<Box style={{ display: 'flex', justifyContent: 'center' }}>
<Text>No item was found</Text>
</Box>
)}
</Box>
</div>
)
}

export default GenericTagGroupItemsField
193 changes: 193 additions & 0 deletions apps/contentful-apps/pages/fields/team-member-filter-tags-field.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
import { useEffect, useState } from 'react'
import { useDebounce } from 'react-use'
import {
CollectionProp,
EntryProps,
KeyValueMap,
QueryOptions,
SysLink,
} from 'contentful-management'
import type { CMAClient, FieldExtensionSDK } from '@contentful/app-sdk'
import { Checkbox, Spinner, Stack, Text } from '@contentful/f36-components'
import { useCMA, useSDK } from '@contentful/react-apps-toolkit'

import { sortAlpha } from '@island.is/shared/utils'

const DEBOUNCE_TIME = 500

const fetchAll = async (cma: CMAClient, query: QueryOptions) => {
let response: CollectionProp<EntryProps<KeyValueMap>> | null = null
const items: EntryProps<KeyValueMap>[] = []
let limit = 100

while ((response === null || items.length < response.total) && limit > 0) {
try {
response = await cma.entry.getMany({
query: {
...query,
limit,
skip: items.length,
},
})
items.push(...response.items)
} catch (error) {
const isResponseTooBig = (error?.message as string)
?.toLowerCase()
?.includes('response size too big')

if (isResponseTooBig) limit = Math.floor(limit / 2)
else throw error
}
}

return items
}

const TeamMemberFilterTagsField = () => {
const sdk = useSDK<FieldExtensionSDK>()
const cma = useCMA()
const [isLoading, setIsLoading] = useState(true)

const [filterTagSysLinks, setFilterTagSysLinks] = useState<SysLink[]>(
sdk.field.getValue() ?? [],
)

const [tagGroups, setTagGroups] = useState<
{
tagGroup: EntryProps<KeyValueMap>
tags: EntryProps<KeyValueMap>[]
}[]
>([])

useEffect(() => {
sdk.window.startAutoResizer()
return () => {
sdk.window.stopAutoResizer()
}
}, [sdk.window])

useEffect(() => {
const fetchTeamList = async () => {
try {
const teamListResponse = await cma.entry.getMany({
query: {
links_to_entry: sdk.entry.getSys().id,
content_type: 'teamList',
},
})

if (teamListResponse.items.length === 0) {
setIsLoading(false)
return
}

const tagGroupSysLinks: SysLink[] =
teamListResponse.items[0].fields.filterGroups?.[
sdk.locales.default
] ?? []

const promises = tagGroupSysLinks.map(async (tagGroupSysLink) => {
const [tagGroup, tags] = await Promise.all([
cma.entry.get({
entryId: tagGroupSysLink.sys.id,
}),
fetchAll(cma, {
links_to_entry: tagGroupSysLink.sys.id,
content_type: 'genericTag',
}),
])

tags.sort((a, b) => {
return sortAlpha(sdk.locales.default)(
a.fields.title,
b.fields.title,
)
})

return { tagGroup, tags }
})

setTagGroups(await Promise.all(promises))
} finally {
setIsLoading(false)
}
}

fetchTeamList()
}, [cma, sdk.entry, sdk.locales.default, setTagGroups])

useDebounce(
() => {
sdk.field.setValue(filterTagSysLinks)
},
DEBOUNCE_TIME,
[filterTagSysLinks],
)

return (
<>
{isLoading && <Spinner />}

{!isLoading && (
<Stack
flexDirection="column"
alignItems="flex-start"
spacing="spacing2Xl"
>
{tagGroups.map(({ tagGroup, tags }) => {
return (
<Stack
key={tagGroup.sys.id}
flexDirection="column"
alignItems="flex-start"
>
<Text fontSize="fontSizeL" fontWeight="fontWeightDemiBold">
{tagGroup.fields.title[sdk.locales.default]}
</Text>
<Stack flexDirection="column" alignItems="flex-start">
{tags.map((tag) => {
const isChecked = filterTagSysLinks.some(
(filterTagSysLink) =>
filterTagSysLink.sys.id === tag.sys.id,
)
return (
<Checkbox
key={tag.sys.id}
isChecked={isChecked}
onChange={() => {
setFilterTagSysLinks((prev) => {
const alreadyExists = prev.some(
(filterTagSysLink) =>
filterTagSysLink.sys.id === tag.sys.id,
)
if (alreadyExists) {
return prev.filter(
(filterTagSysLink) =>
filterTagSysLink.sys.id !== tag.sys.id,
)
}
return prev.concat({
sys: {
id: tag.sys.id,
type: 'Link',
linkType: 'Entry',
},
})
})
}}
>
{tag.fields.title[sdk.locales.default]}
</Checkbox>
)
})}
</Stack>
</Stack>
)
})}
</Stack>
)}
</>
)
}

export default TeamMemberFilterTagsField
Loading

0 comments on commit 2041177

Please sign in to comment.