Skip to content

Commit df43ff6

Browse files
JuliRossiFBanfiCopilot
authored
Bulk Edit v1.1 [] (#10088)
* Bulk-Edit-App: Freeze top row with Field Names [INTEG-2953] (#10076) * freeze top row with Field Names * removing unused import * Bulk-Edit-App: Changing the spinner for a skeleton [INTEG-3104] (#10081) * changing the spinner for a skeleton * rename css style * making the status column sticky too (#10082) * Bulk edit [INTEG-2847] (#10074) * Enhance performance and reduce errors by batching * Adding create-entries and delete-entries v1 scripts * Fixing cors size limitation * moving scripts * Minor fixes * Tests and refactor * Update .env.example to clarify optional entry count setting * Fixing truncation for some really big texts * Adding delete-entries modifications and tests * generation with numbers of entries * batching for getting large entries * simplifying a bit * disabling cancel botton when undoing and saving * Set truncate to 30 * Fixing config files * making test better and simpler * remove duplication * making adjustments base on comments * Refactor entry fetching to handle batch sizes and improve response limits --------- Co-authored-by: francobanfi <franco.banfi@external.contentful.com> * Bulk-Edit-App: Show the progress of the edition [INTEG-3106] (#10085) * adding progress in modal and tests * formating with prettier * creating generic progress component + styling + tests * formating with prettier * using existing note component * upload script (#10086) * Bulk-Edit-App: Adding progress note in undo update [INTEG-3112] (#10087) * Adding progress note in undo update * Update apps/bulk-edit/src/locations/Page/components/UndoBulkEditModal.tsx Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Franco Banfi <62450599+FBanfi@users.noreply.github.com> Co-authored-by: francobanfi <franco.banfi@external.contentful.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent 781afb1 commit df43ff6

23 files changed

+2085
-107
lines changed

apps/bulk-edit/.env.example

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# Content Management API token
2+
# Get this from: https://app.contentful.com/spaces/{SPACE_ID}/api/keys
3+
CONTENTFUL_ACCESS_TOKEN=your_content_management_api_token_here
4+
5+
# Your Contentful Space ID
6+
# Found in the URL when you're in your Contentful space
7+
SPACE_ID=your_space_id_here
8+
9+
# Your Contentful Environment ID
10+
# Usually 'master' for production, or your custom environment name
11+
ENVIRONMENT_ID=master
12+
13+
# Your Contentful Organization ID
14+
# Found in the URL when you're in your organization settings
15+
CONTENTFUL_ORG_ID=your_organization_id_here
16+
17+
# Your Contentful App Definition ID
18+
# Found under the title from the app definition in contentful
19+
CONTENTFUL_APP_DEF_ID=yout_app_definition_id
20+
21+
# Set the amount of entries we want create (OPTIONAL)
22+
AMOUNT_OF_ENTRIES=5
23+
24+
# Set the Content Type name of the entries you want to delete
25+
DELETE_CONTENT_TYPE_NAME="test-content-type"

apps/bulk-edit/.env.test

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# Content Management API token
2+
# Get this from: https://app.contentful.com/spaces/{SPACE_ID}/api/keys
3+
CONTENTFUL_ACCESS_TOKEN=access_token
4+
5+
# Your Contentful Space ID
6+
# Found in the URL when you're in your Contentful space
7+
SPACE_ID=space_id
8+
9+
# Your Contentful Environment ID
10+
# Usually 'master' for production, or your custom environment name
11+
ENVIRONMENT_ID=master
12+
13+
# Your Contentful Organization ID
14+
# Found in the URL when you're in your organization settings
15+
CONTENTFUL_ORG_ID=org_id
16+
17+
# Your Contentful App Definition ID
18+
# Found under the title from the app definition in contentful
19+
CONTENTFUL_APP_DEF_ID=app_deff_id
20+
21+
# Set the amount of entries we want create
22+
AMOUNT_OF_ENTRIES=5
23+
24+
# Set the Content Type name of the entries you want to delete
25+
DELETE_CONTENT_TYPE_NAME="test-content-type"

apps/bulk-edit/.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
.env
1616
.env.*
1717
!.env*.example
18+
!.env.test
1819

1920
# misc
2021
.DS_Store
@@ -23,3 +24,4 @@
2324
npm-debug.log*
2425
yarn-debug.log*
2526
yarn-error.log*
27+

apps/bulk-edit/package-lock.json

Lines changed: 44 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

apps/bulk-edit/package.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,10 @@
2525
"create-app-definition": "contentful-app-scripts create-app-definition",
2626
"add-locations": "contentful-app-scripts add-locations",
2727
"deploy": "contentful-app-scripts upload --ci --bundle-dir ./build --organization-id ${DEFINITIONS_ORG_ID} --definition-id 3bRpNGJb2qXeYUmMkDMAwh --token ${CONTENTFUL_CMA_TOKEN}",
28-
"deploy:staging": "contentful-app-scripts upload --ci --bundle-dir ./dist --organization-id ${TEST_ORG_ID} --definition-id 75CMBc0rQykFfUPGGrECFF --token ${TEST_CMA_TOKEN}"
28+
"deploy:staging": "contentful-app-scripts upload --ci --bundle-dir ./build --organization-id ${TEST_ORG_ID} --definition-id 75CMBc0rQykFfUPGGrECFF --token ${TEST_CMA_TOKEN}",
29+
"upload": "contentful-app-scripts upload --bundle-dir ./build",
30+
"generate-entries": "tsx src/scripts/generateEntries.ts",
31+
"delete-entries": "tsx src/scripts/deleteEntries.ts"
2932
},
3033
"eslintConfig": {
3134
"extends": "react-app"
@@ -52,6 +55,7 @@
5255
"@vitejs/plugin-react": "^4.0.3",
5356
"cross-env": "7.0.3",
5457
"jsdom": "^26.0.0",
58+
"tsx": "^4.7.0",
5559
"typescript": "4.9.5",
5660
"vite": "^6.2.2",
5761
"vitest": "^3.0.9"
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import tokens from '@contentful/f36-tokens';
2+
3+
export const styles = {
4+
editProgress: {
5+
background: tokens.gray100,
6+
border: `1px solid ${tokens.gray300}`,
7+
borderRadius: tokens.borderRadiusMedium,
8+
padding: tokens.spacingM,
9+
},
10+
} as const;

apps/bulk-edit/src/locations/Page/components/BulkEditModal.tsx

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,16 @@
11
import React, { useEffect, useState } from 'react';
2-
import { Modal, Button, TextInput, Text, Flex, FormControl } from '@contentful/f36-components';
2+
import {
3+
Modal,
4+
Button,
5+
TextInput,
6+
Text,
7+
Flex,
8+
FormControl,
9+
Note,
10+
} from '@contentful/f36-components';
311
import type { Entry, ContentTypeField } from '../types';
4-
import { getEntryFieldValue } from '../utils/entryUtils';
12+
import { getEntryFieldValue, truncate } from '../utils/entryUtils';
13+
import { ClockIcon } from '@contentful/f36-icons';
514

615
interface BulkEditModalProps {
716
isOpen: boolean;
@@ -11,6 +20,8 @@ interface BulkEditModalProps {
1120
selectedField: ContentTypeField | null;
1221
defaultLocale: string;
1322
isSaving: boolean;
23+
totalUpdateCount: number;
24+
editionCount: number;
1425
}
1526

1627
export const BulkEditModal: React.FC<BulkEditModalProps> = ({
@@ -21,6 +32,8 @@ export const BulkEditModal: React.FC<BulkEditModalProps> = ({
2132
selectedField,
2233
defaultLocale,
2334
isSaving,
35+
totalUpdateCount,
36+
editionCount,
2437
}) => {
2538
const [value, setValue] = useState('');
2639
const entryCount = selectedEntries.length;
@@ -39,7 +52,12 @@ export const BulkEditModal: React.FC<BulkEditModalProps> = ({
3952
}, [isOpen]);
4053

4154
return (
42-
<Modal isShown={isOpen} onClose={onClose} size="medium" aria-label={title}>
55+
<Modal
56+
isShown={isOpen}
57+
onClose={onClose}
58+
size="medium"
59+
aria-label={title}
60+
key={`bulk-edit-modal-${isOpen ? 'open' : 'closed'}`}>
4361
<Modal.Header title={title} />
4462
<Modal.Content>
4563
<Flex gap="spacingS" flexDirection="column">
@@ -48,7 +66,7 @@ export const BulkEditModal: React.FC<BulkEditModalProps> = ({
4866
</Text>
4967
<Flex>
5068
<Text>
51-
<Text fontWeight="fontWeightDemiBold">{firstValueToUpdate}</Text>{' '}
69+
<Text fontWeight="fontWeightDemiBold">{truncate(firstValueToUpdate, 100)}</Text>{' '}
5270
{entryCount === 1 ? 'selected' : `selected and ${entryCount - 1} more`}
5371
</Text>
5472
</Flex>
@@ -69,9 +87,18 @@ export const BulkEditModal: React.FC<BulkEditModalProps> = ({
6987
)}
7088
</FormControl>
7189
</Flex>
90+
{totalUpdateCount > 0 && isSaving && (
91+
<Note title="Updating entries" variant="neutral" icon={<ClockIcon variant="muted" />}>
92+
{`${editionCount} of ${totalUpdateCount} completed`}
93+
</Note>
94+
)}
7295
</Modal.Content>
7396
<Modal.Controls>
74-
<Button variant="secondary" onClick={onClose} testId="bulk-edit-cancel">
97+
<Button
98+
variant="secondary"
99+
onClick={onClose}
100+
testId="bulk-edit-cancel"
101+
isDisabled={isSaving}>
75102
Cancel
76103
</Button>
77104
<Button

apps/bulk-edit/src/locations/Page/components/TableHeader.tsx

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import React from 'react';
2-
import { Table, Checkbox, Flex, Text } from '@contentful/f36-components';
2+
import { Table, Checkbox, Flex, Text, Box } from '@contentful/f36-components';
33
import { Tooltip } from '@contentful/f36-tooltip';
44
import { QuestionIcon } from '@phosphor-icons/react';
55
import { ContentTypeField } from '../types';
@@ -22,20 +22,22 @@ export const TableHeader: React.FC<TableHeaderProps> = ({
2222
}) => {
2323
return (
2424
<Table.Head style={styles.tableHead}>
25-
<Table.Row>
26-
{fields.length > 0 && (
27-
<Table.Cell as="th" key={DISPLAY_NAME_COLUMN} style={styles.stickyTableHeader}>
28-
Display name
25+
<Table.Row style={styles.stickyTableRow}>
26+
<Box style={styles.stickyMainColumnsOrFields}>
27+
{fields.length > 0 && (
28+
<Table.Cell as="th" key={DISPLAY_NAME_COLUMN} style={styles.tableHeader}>
29+
Display name
30+
</Table.Cell>
31+
)}
32+
<Table.Cell as="th" key={ENTRY_STATUS_COLUMN} style={styles.tableHeader}>
33+
<Flex gap="spacingXs" alignItems="center" justifyContent="flex-start">
34+
Status
35+
<Tooltip content="Bulk editing is not supported for Status" placement="top">
36+
<QuestionIcon size={16} aria-label="Bulk editing not supported for Status" />
37+
</Tooltip>
38+
</Flex>
2939
</Table.Cell>
30-
)}
31-
<Table.Cell as="th" key={ENTRY_STATUS_COLUMN} style={styles.tableHeader}>
32-
<Flex gap="spacingXs" alignItems="center" justifyContent="flex-start">
33-
Status
34-
<Tooltip content="Bulk editing is not supported for Status" placement="top">
35-
<QuestionIcon size={16} aria-label="Bulk editing not supported for Status" />
36-
</Tooltip>
37-
</Flex>
38-
</Table.Cell>
40+
</Box>
3941
{fields.map((field) => {
4042
const isAllowed = isCheckboxAllowed(field);
4143
const isDisabled = checkboxesDisabled[field.uniqueId];

apps/bulk-edit/src/locations/Page/components/TableRow.tsx

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import React, { useState } from 'react';
2-
import { Table, TextLink, Badge, Checkbox, Flex, Text } from '@contentful/f36-components';
2+
import { Table, TextLink, Badge, Checkbox, Flex, Text, Box } from '@contentful/f36-components';
33
import { ExternalLinkIcon } from '@contentful/f36-icons';
44
import { Entry, ContentTypeField } from '../types';
55
import { ContentTypeProps } from 'contentful-management';
@@ -44,20 +44,22 @@ export const TableRow: React.FC<TableRowProps> = ({
4444

4545
return (
4646
<Table.Row key={entry.sys.id}>
47-
<Table.Cell testId="display-name-cell" style={styles.stickyCell} isTruncated>
48-
<TextLink
49-
href={getEntryUrl(entry, spaceId, environmentId)}
50-
target="_blank"
51-
rel="noopener noreferrer"
52-
testId="entry-link"
53-
icon={<ExternalLinkIcon />}
54-
alignIcon="end">
55-
{getEntryTitle(entry, contentType, defaultLocale)}
56-
</TextLink>
57-
</Table.Cell>
58-
<Table.Cell testId="status-cell" style={styles.cell}>
59-
<Badge variant={status.color}>{status.label}</Badge>
60-
</Table.Cell>
47+
<Box style={styles.stickyMainColumnsOrFields}>
48+
<Table.Cell testId="display-name-cell" style={styles.cell}>
49+
<TextLink
50+
href={getEntryUrl(entry, spaceId, environmentId)}
51+
target="_blank"
52+
rel="noopener noreferrer"
53+
testId="entry-link"
54+
icon={<ExternalLinkIcon />}
55+
alignIcon="end">
56+
{getEntryTitle(entry, contentType, defaultLocale)}
57+
</TextLink>
58+
</Table.Cell>
59+
<Table.Cell testId="status-cell" style={styles.cell}>
60+
<Badge variant={status.color}>{status.label}</Badge>
61+
</Table.Cell>
62+
</Box>
6163
{fields.map((field) => {
6264
const isAllowed = isCheckboxAllowed(field);
6365
const isDisabled = cellCheckboxesDisabled[field.uniqueId];

0 commit comments

Comments
 (0)