Skip to content

Commit

Permalink
Merge branch 'main' into j-s/defender-case-files-connection
Browse files Browse the repository at this point in the history
  • Loading branch information
unakb committed Nov 27, 2024
2 parents bdeea4f + c33e7a2 commit 5bda7ff
Show file tree
Hide file tree
Showing 101 changed files with 4,840 additions and 2,710 deletions.
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

0 comments on commit 5bda7ff

Please sign in to comment.