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

fix: make form-system env var optional #17030

Closed
wants to merge 9 commits into from
155 changes: 155 additions & 0 deletions apps/contentful-apps/components/EntryListSearch.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
import { useRef, useState } from 'react'
import { useDebounce } from 'react-use'
import {
CollectionProp,
EntryProps,
KeyValueMap,
QueryOptions,
} from 'contentful-management'
import {
Box,
EntryCard,
Pagination,
Spinner,
Stack,
Text,
TextInput,
} from '@contentful/f36-components'
import { useCMA, useSDK } from '@contentful/react-apps-toolkit'

import { DEFAULT_LOCALE } from '../constants'

const SEARCH_DEBOUNCE_TIME_IN_MS = 300

interface EntryListSearchProps {
contentTypeId: string
contentTypeLabel: string
contentTypeTitleField: string
itemsPerPage?: number
onEntryClick?: (entry: EntryProps) => void
query?: QueryOptions
}

export const EntryListSearch = ({
itemsPerPage = 4,
contentTypeId,
contentTypeLabel,
contentTypeTitleField,
onEntryClick,
query,
}: EntryListSearchProps) => {
const sdk = useSDK()
const cma = useCMA()

const searchValueRef = useRef('')
const [searchValue, setSearchValue] = useState('')
const [listItemResponse, setListItemResponse] =
useState<CollectionProp<EntryProps<KeyValueMap>>>()
const [loading, setLoading] = useState(false)
const [page, setPage] = useState(0)
const pageRef = useRef(0)
const [counter, setCounter] = useState(0)

const skip = itemsPerPage * page

useDebounce(
async () => {
setLoading(true)
try {
const response = await cma.entry.getMany({
query: {
content_type: contentTypeId,
limit: itemsPerPage,
skip,
[`fields.${contentTypeTitleField}[match]`]: searchValue,
'sys.archivedAt[exists]': false,
...query,
},
})
if (
searchValueRef.current === searchValue &&
pageRef.current === page
) {
setListItemResponse(response)
}
} finally {
setLoading(false)
}
},
SEARCH_DEBOUNCE_TIME_IN_MS,
[page, searchValue, counter],
)

return (
<Box style={{ display: 'flex', flexFlow: 'column nowrap', gap: '24px' }}>
<TextInput
placeholder="Search for an entry"
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: loading ? '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={contentTypeLabel}
title={
item.fields[contentTypeTitleField]?.[DEFAULT_LOCALE] ||
'Untitled'
}
onClick={() => {
if (onEntryClick) {
onEntryClick(item)
return
}

sdk.navigator
.openEntry(item.sys.id, {
slideIn: { waitForClose: true },
})
.then(() => {
setCounter((c) => c + 1)
})
}}
/>
))}
</Stack>
</Box>
<Pagination
activePage={page}
itemsPerPage={itemsPerPage}
totalItems={listItemResponse.total}
onPageChange={(newPage) => {
pageRef.current = newPage
setPage(newPage)
}}
/>
</>
)}

{listItemResponse?.items?.length === 0 && (
<Box style={{ display: 'flex', justifyContent: 'center' }}>
<Text>No entry was found</Text>
</Box>
)}
</Box>
)
}
Original file line number Diff line number Diff line change
@@ -1,27 +1,14 @@
import { useMemo, useRef, useState } from 'react'
import { useDebounce } from 'react-use'
import { CollectionProp, EntryProps, KeyValueMap } from 'contentful-management'
import { useMemo, useState } from 'react'
import dynamic from 'next/dynamic'
import { EditorExtensionSDK } from '@contentful/app-sdk'
import {
Box,
Button,
EntryCard,
FormControl,
Pagination,
Spinner,
Stack,
Text,
TextInput,
} from '@contentful/f36-components'
import { Box, Button, FormControl } from '@contentful/f36-components'
import { PlusIcon } from '@contentful/f36-icons'
import { useCMA, useSDK } from '@contentful/react-apps-toolkit'

import { EntryListSearch } from '../../../../components/EntryListSearch'
import { mapLocalesToFieldApis } from '../../utils'

const SEARCH_DEBOUNCE_TIME_IN_MS = 300
const LIST_ITEM_CONTENT_TYPE_ID = 'genericListItem'
const LIST_ITEMS_PER_PAGE = 4

const createLocaleToFieldMapping = (sdk: EditorExtensionSDK) => {
return {
Expand Down Expand Up @@ -58,51 +45,11 @@ export const GenericListEditor = () => {
const sdk = useSDK<EditorExtensionSDK>()
const cma = useCMA()

const searchValueRef = useRef('')
const [searchValue, setSearchValue] = useState('')
const [listItemResponse, setListItemResponse] =
useState<CollectionProp<EntryProps<KeyValueMap>>>()
const [loading, setLoading] = useState(false)
const [page, setPage] = useState(0)
const pageRef = useRef(0)

/** Counter that's simply used to refresh the list when an item gets created */
const [counter, setCounter] = useState(0)

const skip = LIST_ITEMS_PER_PAGE * page
const [_, setCounter] = useState(0)

const defaultLocale = sdk.locales.default

useDebounce(
async () => {
setLoading(true)
try {
const response = await cma.entry.getMany({
environmentId: sdk.ids.environment,
spaceId: sdk.ids.space,
query: {
content_type: LIST_ITEM_CONTENT_TYPE_ID,
limit: LIST_ITEMS_PER_PAGE,
skip,
'fields.internalTitle[match]': searchValue,
'fields.genericList.sys.id': sdk.entry.getSys().id,
'sys.archivedAt[exists]': false,
},
})
if (
searchValueRef.current === searchValue &&
pageRef.current === page
) {
setListItemResponse(response)
}
} finally {
setLoading(false)
}
},
SEARCH_DEBOUNCE_TIME_IN_MS,
[page, searchValue, counter],
)

const createListItem = async () => {
const cardIntro = {}

Expand Down Expand Up @@ -189,70 +136,14 @@ export const GenericListEditor = () => {
<Button startIcon={<PlusIcon />}>Add item</Button>
</Box>
</Box>
<Box style={{ display: 'flex', flexFlow: 'column nowrap', gap: '24px' }}>
<TextInput
placeholder="Search for a list item"
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: loading ? '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="List Item"
title={
item.fields.internalTitle?.[defaultLocale] ?? '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>
<EntryListSearch
contentTypeId={LIST_ITEM_CONTENT_TYPE_ID}
contentTypeLabel="List Item"
contentTypeTitleField="internalTitle"
query={{
'fields.genericList.sys.id': sdk.ids.entry,
}}
/>
</Box>
)
}
Loading