* fix(deps): update dependency @tanstack/react-query to v5 * chore: update for compatibility with React Query v5 * chore: update for compatibility with React Query v5 * test: update tests --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Braden MacDonald <braden@opencraft.com>
134 lines
3.7 KiB
TypeScript
134 lines
3.7 KiB
TypeScript
import { useIntl, FormattedMessage } from '@edx/frontend-platform/i18n';
|
|
import {
|
|
Alert,
|
|
Card,
|
|
Form,
|
|
Pagination,
|
|
SearchField,
|
|
Stack,
|
|
} from '@openedx/paragon';
|
|
import { useCallback, useState } from 'react';
|
|
|
|
import Loading from '../../generic/Loading';
|
|
import AlertError from '../../generic/alert-error';
|
|
import { useContentLibraryV2List } from '../data/apiHooks';
|
|
import messages from './messages';
|
|
import { ContentType } from '../routes';
|
|
|
|
interface EmptyStateProps {
|
|
hasSearchQuery: boolean;
|
|
}
|
|
|
|
const EmptyState = ({ hasSearchQuery }: EmptyStateProps) => (
|
|
<Alert className="mt-4 align-self-center">
|
|
<Alert.Heading>
|
|
{hasSearchQuery ? (
|
|
<FormattedMessage {...messages.selectLibraryNoSearchResultsTitle} />
|
|
) : (
|
|
<FormattedMessage {...messages.selectLibraryNoLibrariesTitle} />
|
|
)}
|
|
</Alert.Heading>
|
|
<p>
|
|
{hasSearchQuery ? (
|
|
<FormattedMessage {...messages.selectLibraryNoSearchResultsMessage} />
|
|
) : (
|
|
<FormattedMessage {...messages.selectLibraryNoLibrariesMessage} />
|
|
)}
|
|
</p>
|
|
</Alert>
|
|
);
|
|
|
|
interface SelectLibraryProps {
|
|
selectedLibrary: string;
|
|
setSelectedLibrary: (libraryKey: string) => void;
|
|
itemType: ContentType;
|
|
}
|
|
|
|
const SelectLibrary = ({ selectedLibrary, setSelectedLibrary, itemType }: SelectLibraryProps) => {
|
|
const intl = useIntl();
|
|
|
|
const [searchQuery, setSearchQuery] = useState('');
|
|
const [currentPage, setCurrentPage] = useState(1);
|
|
|
|
const handleSearch = useCallback((search: string) => {
|
|
setSearchQuery(search);
|
|
setCurrentPage(1);
|
|
}, []);
|
|
|
|
const {
|
|
data,
|
|
isPending,
|
|
isError,
|
|
error,
|
|
} = useContentLibraryV2List({
|
|
page: currentPage,
|
|
pageSize: 5,
|
|
search: searchQuery,
|
|
});
|
|
|
|
if (isError) {
|
|
return <AlertError error={error} />;
|
|
}
|
|
|
|
if (isPending) {
|
|
return <Loading />;
|
|
}
|
|
|
|
return (
|
|
<Stack gap={2} className="p-5">
|
|
<small className="text-primary-700">
|
|
<FormattedMessage
|
|
{...messages[`selectLibraryInfo.${itemType}`]}
|
|
/>
|
|
</small>
|
|
<SearchField
|
|
onSubmit={handleSearch}
|
|
onChange={handleSearch}
|
|
value={searchQuery}
|
|
placeholder={intl.formatMessage(messages.selectLibrarySearchPlaceholder)}
|
|
/>
|
|
{data.results.length === 0 ? (<EmptyState hasSearchQuery={!!searchQuery} />) : (
|
|
<>
|
|
<Form.RadioSet
|
|
name="selected-library"
|
|
onChange={(e: React.ChangeEvent<HTMLInputElement>) => setSelectedLibrary(e.target.value)}
|
|
value={selectedLibrary}
|
|
className="mt-4"
|
|
>
|
|
{data.results.map((library) => (
|
|
<Card
|
|
key={library.id}
|
|
isClickable
|
|
onClick={() => setSelectedLibrary(library.id)}
|
|
className="card-item"
|
|
>
|
|
<Card.Header
|
|
size="sm"
|
|
title={<span className="card-item-title">{library.title}</span>}
|
|
subtitle={`${library.org} / ${library.slug}`}
|
|
actions={(
|
|
<Form.Radio value={library.id} name={`select-library-${library.id}`}>{' '}</Form.Radio>
|
|
)}
|
|
/>
|
|
<Card.Body>
|
|
<p>{library.description}</p>
|
|
</Card.Body>
|
|
</Card>
|
|
))}
|
|
</Form.RadioSet>
|
|
<Pagination
|
|
paginationLabel={intl.formatMessage(messages.selectLibraryPaginationLabel)}
|
|
pageCount={data!.numPages}
|
|
currentPage={data!.currentPage}
|
|
onPageSelect={setCurrentPage}
|
|
variant="secondary"
|
|
className="align-self-center"
|
|
/>
|
|
</>
|
|
)}
|
|
</Stack>
|
|
);
|
|
};
|
|
|
|
export default SelectLibrary;
|