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: adapt-essentials-asset-fields search and bugfixes #1828

Merged
merged 21 commits into from
May 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
129af0d
fix: Remove phantom locales by filtering on existing ones and scoping…
zzzarius Feb 12, 2024
4f0b419
fix: Use 3000 port for preview
zzzarius Feb 12, 2024
1349fb1
refactor: Extract styles into css module & small misc fixes
zzzarius Feb 12, 2024
53850a1
fix: Add case for when image is not uploaded
zzzarius Mar 8, 2024
a1bc832
fix: Replace useCMA with custom hook
zzzarius May 15, 2024
0b99fbb
fix: Wrap cma with useMemo to prevent unnecessary renderings
zzzarius May 15, 2024
44524a6
fix: Default to empty object for JSON.parse on error
zzzarius May 15, 2024
8ecacea
feat: useQuery hook
zzzarius May 16, 2024
1832587
fix: Wrap page with the store provider and add actions
zzzarius May 16, 2024
52c7b1d
feat: Add ability to query assets
zzzarius May 16, 2024
27511c0
feat: Add query to context
zzzarius May 16, 2024
9581a24
feat: Reimplement media search styles
zzzarius May 16, 2024
b3ee0f4
feat: Tag chooser
zzzarius May 16, 2024
eb48c6a
feat: Asset filters
zzzarius May 16, 2024
ba42128
refactor: Extract Advanced search into standalone component
zzzarius May 16, 2024
7a1fffb
fix: Don't check checkbox when there are no entries
zzzarius May 16, 2024
cb29377
fix: Unable to publish if there is no file
zzzarius May 16, 2024
19b8cb3
fix: Show text when there are no entries
zzzarius May 16, 2024
dc32a1e
fix: Move WorkbenchActions styles into module
zzzarius May 16, 2024
a7b663a
fix: Remove unused instance variable
zzzarius May 16, 2024
8541fb3
fix: Reset page and limit when searching
zzzarius May 17, 2024
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
@@ -0,0 +1,96 @@
import { Button, Flex, Select, Stack, TextInput } from '@contentful/f36-components';
import { FilterIcon, PlusIcon } from '@contentful/f36-icons';
import { css } from 'emotion';
import tokens from '@contentful/f36-tokens';
import { useState } from 'react';

export function AdvancedSearch() {
const [searchTerm, setSearchTerm] = useState('');

const styles = {
searchWrapper: css({
border: `1px solid ${tokens.gray200}`,
borderRadius: '0.3rem',
}),
searchInput: css({
border: 'none',
}),
searchInputFilter: css({
border: 'none',
fontSize: '0.8rem',
color: tokens.blue600,
'&:hover': {
color: tokens.blue600,
},
}),
icon: css({
border: `1px solid ${tokens.gray600}`,
borderRadius: '50%',
}),
filterButton: css({
border: `1px dashed ${tokens.gray600}`,
borderRadius: '0.8rem',
padding: '0.2rem 0.5rem 0.2rem 0.2rem',
minHeight: '0',
fontSize: '0.8rem',
color: tokens.gray600,
backgroundColor: tokens.gray100,
}),
filters: css({
marginTop: '0.3rem',
}),
};

return (
<Flex flexDirection="column">
<TextInput.Group className={styles.searchWrapper}>
<Flex gap="1rem">
<Flex>
<Flex alignItems="center">filterName</Flex>
<Select id="optionSelect-controlled" name="optionSelect-controlled" value={''}>
<Select.Option value="optionOne">Option 1</Select.Option>
<Select.Option value="optionTwo">Long Option 2</Select.Option>
</Select>
<Select id="optionSelect-controlled" name="optionSelect-controlled" value={''}>
<Select.Option value="optionOne">Option 1</Select.Option>
<Select.Option value="optionTwo">Long Option 2</Select.Option>
</Select>
</Flex>
<Flex>
<Flex alignItems="center">filterName</Flex>
<Select id="optionSelect-controlled" name="optionSelect-controlled" value={''}>
<Select.Option value="optionOne">Option 1</Select.Option>
<Select.Option value="optionTwo">Long Option 2</Select.Option>
</Select>
<Select id="optionSelect-controlled" name="optionSelect-controlled" value={''}>
<Select.Option value="optionOne">Option 1</Select.Option>
<Select.Option value="optionTwo">Long Option 2</Select.Option>
</Select>
</Flex>
</Flex>
<TextInput
id="search"
name="search"
className={styles.searchInput}
placeholder="Type to search for assets"
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
/>
<Button variant="secondary" className={styles.searchInputFilter} startIcon={<FilterIcon />} onClick={() => {}} aria-label="Filter">
Filter
</Button>
</TextInput.Group>
<Stack className={styles.filters}>
<Button variant="secondary" size="small" className={styles.filterButton} startIcon={<PlusIcon className={styles.icon} />}>
Created by me
</Button>
<Button variant="secondary" className={styles.filterButton} startIcon={<PlusIcon className={styles.icon} />}>
Status is
</Button>
<Button variant="secondary" className={styles.filterButton} startIcon={<PlusIcon className={styles.icon} />}>
Tag is one of
</Button>
</Stack>
</Flex>
);
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import { useDebounce } from '@uidotdev/usehooks';
import { useState, useEffect } from 'react';
import { useCMA, useSDK } from '@contentful/react-apps-toolkit';
import { useSDK } from '@contentful/react-apps-toolkit';
import { useCMA } from './hooks/useCMA';
import { PageAppSDK } from '@contentful/app-sdk';
import { TextInput, Textarea, FormControl, Caption } from '@contentful/f36-components';
import { AssetProps } from 'contentful-management/dist/typings/entities/asset';
import { extractContentfulFieldError } from './utils/entries.ts';
import useLocales from './hooks/useLocales.tsx';
import useAssetEntries from './hooks/useAssetEntries.tsx';
import styles from './styles.module.css';

interface AssetInputFieldTextComponentProps {
asset: AssetProps;
Expand All @@ -15,9 +17,12 @@ interface AssetInputFieldTextComponentProps {
rows?: number;
as?: 'TextInput' | 'Textarea';
showLocaleLabel?: boolean;
isDisabled?: boolean;
}

const AssetInputFieldTextComponent = ({ asset, field, locale, as = 'TextInput', rows = 1, showLocaleLabel = false }: AssetInputFieldTextComponentProps) => {
const AssetInputFieldTextComponent = ({
asset, field, locale, as = 'TextInput', rows = 1, showLocaleLabel = false, isDisabled = false
}: AssetInputFieldTextComponentProps) => {
const fieldValueProp = asset.fields[field]?.[locale] ?? asset.fields.file?.[locale]?.[field] ?? '';
const assetId = asset.sys.id;
const { updateAssetEntry } = useAssetEntries();
Expand Down Expand Up @@ -82,15 +87,23 @@ const AssetInputFieldTextComponent = ({ asset, field, locale, as = 'TextInput',
}, [debouncedFieldValue]);

const { localeNames } = useLocales();
const InputComponent = as === 'Textarea' ? Textarea : TextInput;
const isTextarea = as === 'Textarea';
const InputComponent = isTextarea ? Textarea : TextInput;
const inputChangeHandler = (event) => {
setNewFieldValue(event.target.value);
};

return (
<>
{showLocaleLabel && <Caption>{localeNames[locale]}</Caption>}
<InputComponent key={`${assetId}-${locale}`} value={newFieldValue} onChange={inputChangeHandler} rows={rows} />
<InputComponent
key={`${assetId}-${locale}`}
value={newFieldValue}
onChange={inputChangeHandler}
rows={rows}
isDisabled={isDisabled}
className={isTextarea ? styles.textarea : styles.textInput}
/>
{error && <FormControl.ValidationMessage>{error}</FormControl.ValidationMessage>}
</>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { EntryStatus } from './EntryStatus';
import useLocales from './hooks/useLocales';
import { AvailableColumns } from './hooks/useColumns';
import useUser from './hooks/useUser';
import styles from './styles.module.css';

interface BodyInputCellResolverProps {
column: AvailableColumns;
Expand All @@ -30,8 +31,8 @@ export const BodyInputCellResolver = ({ column, asset, loading = false, ...rest

if (loading && column !== 'status')
return (
<TableCell key={column} {...rest}>
<SkeletonContainer style={{ height: '60px' }}>
<TableCell key={column} {...rest} className={[styles.cellSkeleton, rest.className].filter(Boolean).join(' ')}>
<SkeletonContainer className={styles.cellSkeletonContainer}>
<SkeletonBodyText numberOfLines={2} />
</SkeletonContainer>
</TableCell>
Expand All @@ -53,7 +54,12 @@ export const BodyInputCellResolver = ({ column, asset, loading = false, ...rest
case 'filename':
return (
<TableCell key={column} {...rest}>
<AssetInputFieldText asset={asset} locales={enabledLocales} field={'fileName'} />
<AssetInputFieldText
asset={asset}
locales={enabledLocales.filter((locale) => asset.fields?.file?.[locale]?.fileName)}
field={'fileName'}
isDisabled={true}
/>
</TableCell>
);
case 'createdAt':
Expand All @@ -73,7 +79,13 @@ export const BodyInputCellResolver = ({ column, asset, loading = false, ...rest
<TableCell key={column} {...rest}>
<Flex gap="spacingXs" alignItems="center">
{user?.avatarUrl && (
<Image src={user.avatarUrl} height="25px" width="25px" style={{ borderRadius: '50%' }} alt={`${user.firstName} ${user.lastName}`} />
<Image
className={styles.avatar}
src={user.avatarUrl}
height="24px"
width="24px"
alt={`${user.firstName} ${user.lastName}`}
/>
)}
{user?.firstName && user?.lastName && (
<Text fontColor="gray900">
Expand All @@ -85,10 +97,10 @@ export const BodyInputCellResolver = ({ column, asset, loading = false, ...rest
);
case 'status':
return (
<TableCell key={column} style={{ width: '50px', maxWidth: '50px' }} {...rest}>
<TableCell key={column} className={styles.statusCell} {...rest}>
{!loading && <EntryStatus sys={asset.sys} />}
{loading && (
<SkeletonContainer style={{ height: '60px' }}>
<SkeletonContainer className={styles.statusSkeleton}>
<SkeletonDisplayText />
</SkeletonContainer>
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import TableBody from './TableBody';
import VisibleColumnsCell from './VisibleColumnsCell';
import Paginator from './Paginator';
import useUsers from './hooks/useUsers';
import styles from './styles.module.css';

export default function Dashboard() {
useUsers();
Expand All @@ -16,8 +17,8 @@ export default function Dashboard() {
<Box marginTop="spacingXl">
<Table>
<Table.Head>
<Table.Row style={{ position: 'sticky', top: '-35px', zIndex: '1' }}>
<Table.Cell style={{ verticalAlign: 'middle' }}>
<Table.Row className={styles.dashboardRow}>
<Table.Cell className={styles.dashboardCheckboxCell}>
<SelectAllCheckbox />
</Table.Cell>
<VisibleColumnsCell />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { Pagination } from '@contentful/f36-components';
import { useCMA } from '@contentful/react-apps-toolkit';
import { useEffect } from 'react';
import useEntriesSelection from './hooks/useEntriesSelection';
import useAssetEntries from './hooks/useAssetEntries';
Expand All @@ -9,8 +8,11 @@ import useActivePage from './hooks/useActivePage';
import useSkip from './hooks/useSkip';
import useOrder from './hooks/useOrder';
import useLimit from './hooks/useLimit';
import { useCMA } from './hooks/useCMA';
import useQuery from './hooks/useQuery';

const Paginator = () => {
const cma = useCMA();
const { setIsLoading } = useEntriesLoading();
const { total, setTotal } = useTotal();
const { activePage, setActivePage } = useActivePage();
Expand All @@ -20,7 +22,7 @@ const Paginator = () => {
const { assetEntries, setAssetEntries } = useAssetEntries();
const { setSelectedEntries } = useEntriesSelection();
const { selectedEntries } = useEntriesSelection();
const cma = useCMA();
const { query } = useQuery();

useEffect(() => {
setIsLoading(true);
Expand All @@ -36,6 +38,7 @@ const Paginator = () => {
skip,
limit,
order,
query,
},
});
setTotal(assetResponse.total);
Expand All @@ -44,7 +47,7 @@ const Paginator = () => {
}
fetchData();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [cma.asset, setAssetEntries, skip, limit, order]);
}, [cma.asset, setAssetEntries, skip, limit, order, query]);

const pageChangeHandler = (activePage) => {
setActivePage(activePage);
Expand All @@ -54,6 +57,7 @@ const Paginator = () => {
return (
selectedEntries.length < 1 && (
<Pagination
key={query}
activePage={activePage}
onPageChange={pageChangeHandler}
isLastPage={total <= activePage * limit}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import useAssetEntries from './hooks/useAssetEntries';
const SelectAllCheckbox = () => {
const { assetEntries } = useAssetEntries();
const { selectedEntries, setSelectedEntries } = useEntriesSelection();
const selectedAll = selectedEntries.length === assetEntries.length;
const selectedAll = selectedEntries.length > 0 && selectedEntries.length === assetEntries.length;
return (
<Checkbox
isChecked={selectedAll}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import useEntriesSelection from './hooks/useEntriesSelection';
import useColumns from './hooks/useColumns';
import useAssetEntries from './hooks/useAssetEntries';
import { EntryStatus, getEntryStatus } from './utils/entries';
import { useCMA, useSDK } from '@contentful/react-apps-toolkit';
import { useSDK } from '@contentful/react-apps-toolkit';
import { useCMA } from './hooks/useCMA';
import { AssetProps } from 'contentful-management';

const SelectionControlsTableRow = () => {
Expand Down Expand Up @@ -33,12 +34,13 @@ const SelectionControlsTableRow = () => {

const republishableAssets = useMemo(() => {
return selectedAssets.filter(
(assetEntry) => getEntryStatus(assetEntry.sys) === EntryStatus.CHANGED || getEntryStatus(assetEntry.sys) === EntryStatus.DRAFT,
(assetEntry) =>
getEntryStatus(assetEntry.sys) === EntryStatus.CHANGED || (getEntryStatus(assetEntry.sys) === EntryStatus.DRAFT && assetEntry.fields.file),
);
}, [selectedAssets]);

const publishableAssets = useMemo(() => {
return selectedAssets.filter((assetEntry) => getEntryStatus(assetEntry.sys) === EntryStatus.DRAFT);
return selectedAssets.filter((assetEntry) => getEntryStatus(assetEntry.sys) === EntryStatus.DRAFT && assetEntry.fields.file);
}, [selectedAssets]);

const archivableAssets = useMemo(() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { useState } from 'react';
import useLocales from './hooks/useLocales';
import useColumns from './hooks/useColumns';
import useLimit from './hooks/useLimit';
import styles from './styles.module.css';

export const SettingsPopover = () => {
const [showSettingsPopup, setShowSettingsPopup] = useState(false);
Expand All @@ -13,7 +14,8 @@ export const SettingsPopover = () => {
const { limit, setLimit } = useLimit();

const handlePageSizeChange = (event) => {
setLimit(Number(event.target.value));
const value = Number(event.target.value);
setLimit(value);
};

return (
Expand All @@ -30,7 +32,7 @@ export const SettingsPopover = () => {
<Popover.Content>
<FocusLock>
<Stack padding="spacingM" margin="none" spacing="spacingS" flexDirection="column" paddingBottom="none">
<Box style={{ alignSelf: 'flex-start', fontWeight: '600' }}>Columns</Box>
<Box className={styles.settingsPopoverLabel}>Columns</Box>
{columns.map((column) => {
const { label, isVisible } = columnDetails[column];
return (
Expand All @@ -47,15 +49,9 @@ export const SettingsPopover = () => {
})}
</Stack>
<Stack padding="spacingM" margin="none" spacing="spacingS" flexDirection="column">
<Box style={{ alignSelf: 'flex-start', fontWeight: '600' }}>Locales</Box>
<Box className={styles.settingsPopoverLabel}>Locales</Box>
{locales.map((locale) => (
<Flex
key={locale}
fullWidth
style={{
display: 'flex',
justifyContent: 'flex-start',
}}>
<Flex key={locale} fullWidth className={styles.localeSwitchWrapper}>
<Switch
size="small"
isChecked={enabledLocales.includes(locale)}
Expand All @@ -65,11 +61,9 @@ export const SettingsPopover = () => {
</Switch>
</Flex>
))}
<Box className={styles.settingsPopoverLabel}>Page size</Box>
<TextInput type="number" min={5} max={100} className={styles.pageSizeInput} value={String(limit)} onChange={handlePageSizeChange} />
</Stack>
<Box padding="spacingM" margin="none" flexDirection="column">
<Box marginBottom="spacingS">Page size</Box>
<TextInput type="number" min={5} max={100} style={{ maxWidth: 'fit-content' }} value={String(limit)} onChange={handlePageSizeChange} />
</Box>
</FocusLock>
</Popover.Content>
</Popover>
Expand Down
Loading