Skip to content

Commit

Permalink
fix(app-headless-cms): delete multiple entries sent wrong id (#3875)
Browse files Browse the repository at this point in the history
  • Loading branch information
brunozoric authored Feb 21, 2024
1 parent 50b80c7 commit fb21e26
Show file tree
Hide file tree
Showing 6 changed files with 188 additions and 40 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { observer } from "mobx-react-lite";
import { ContentEntryListConfig } from "~/admin/config/contentEntries";
import { useCms, useContentEntry } from "~/admin/hooks";
import { getEntriesLabel } from "~/admin/components/ContentEntries/BulkActions/BulkActions";
import { parseIdentifier } from "@webiny/utils/parseIdentifier";

export const ActionDelete = observer(() => {
const { deleteEntry } = useCms();
Expand All @@ -28,10 +29,15 @@ export const ActionDelete = observer(() => {
execute: async () => {
await worker.processInSeries(async ({ item, report }) => {
try {
/**
* We need an entryId because we want to delete all revisions of the entry.
* By sending an entryId (id without #version), we are telling to the API to delete all revisions.
*/
const { id } = parseIdentifier(item.id);
const response = await deleteEntry({
model: contentModel,
entry: item,
id: item.id
id
});

const { error } = response;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,64 @@ import {
CmsModel,
CmsModelField
} from "~/types";
import { makeDecoratable } from "@webiny/react-composition";

const BottomMargin = styled.div`
margin-bottom: 20px;
`;

type GetBind = CmsModelFieldRendererProps["getBind"];

export interface MultiValueItemContainerProps {
value: TemplateValue;
contentModel: CmsModel;
isFirst: boolean;
isLast: boolean;
onMoveUp: () => void;
onMoveDown: () => void;
onDelete: () => void;
onClone: () => void;
title: React.ReactNode;
description: string;
icon: JSX.Element;
actions: JSX.Element;
template: CmsDynamicZoneTemplate;
children: React.ReactNode;
}

export const MultiValueItemContainer = makeDecoratable(
"MultiValueItemContainer",
({ title, description, icon, actions, children }: MultiValueItemContainerProps) => {
return (
<AccordionItem title={title} description={description} icon={icon} actions={actions}>
{children}
</AccordionItem>
);
}
);

export interface MultiValueItemItemProps {
template: CmsDynamicZoneTemplate;
contentModel: CmsModel;
Bind: BindComponent;
}

export const MultiValueItem = makeDecoratable(
"MultiValueItem",
(props: MultiValueItemItemProps) => {
const { template, Bind, contentModel } = props;

return (
<Fields
fields={template.fields}
layout={template.layout || []}
contentModel={contentModel}
Bind={Bind}
/>
);
}
);

interface TemplateValue {
_templateId: string;
[key: string]: any;
Expand Down Expand Up @@ -65,10 +116,19 @@ const TemplateValueForm = ({
}

return (
<AccordionItem
<MultiValueItemContainer
value={value}
contentModel={contentModel}
onClone={onClone}
isFirst={isFirst}
isLast={isLast}
onDelete={onDelete}
onMoveUp={onMoveUp}
onMoveDown={onMoveDown}
title={template.name}
description={template.description}
icon={<TemplateIcon icon={template.icon} />}
template={template}
actions={
<AccordionItem.Actions>
<AccordionItem.Action
Expand All @@ -87,28 +147,35 @@ const TemplateValueForm = ({
</AccordionItem.Actions>
}
>
<Fields
fields={template.fields}
layout={template.layout || []}
contentModel={contentModel}
Bind={Bind}
/>
</AccordionItem>
<MultiValueItem template={template} contentModel={contentModel} Bind={Bind} />
</MultiValueItemContainer>
);
};

export interface MultiValueContainerProps extends MultiValueDynamicZoneProps {
children: React.ReactNode;
}

export const MultiValueContainer = makeDecoratable<
React.FunctionComponent<MultiValueContainerProps>
>("MultiValueContainer", ({ children }) => {
return (
<Accordion>
<>{children}</>
</Accordion>
);
});

interface MultiValueDynamicZoneProps {
// TODO: this prop might be useless, because we now have a `useModelField` hook.
field: CmsModelField;
bind: BindComponentRenderProp;
contentModel: CmsModel;
getBind: GetBind;
}

export const MultiValueDynamicZone = ({
bind,
getBind,
contentModel
}: MultiValueDynamicZoneProps) => {
export const MultiValueDynamicZone = (props: MultiValueDynamicZoneProps) => {
const { bind, getBind, contentModel } = props;
const onTemplate = (template: CmsDynamicZoneTemplate) => {
bind.appendValue({ _templateId: template.id });
};
Expand All @@ -124,7 +191,7 @@ export const MultiValueDynamicZone = ({
return (
<>
{hasValues ? (
<Accordion>
<MultiValueContainer {...props}>
{values.map((value, index) => (
<TemplateValueForm
key={index}
Expand All @@ -139,7 +206,7 @@ export const MultiValueDynamicZone = ({
onClone={() => cloneValue(index)}
/>
))}
</Accordion>
</MultiValueContainer>
) : null}
{hasValues ? (
<AddTemplateIcon onTemplate={onTemplate} />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,68 @@
import React from "react";
import { css } from "emotion";
import { Accordion, AccordionItem } from "@webiny/ui/Accordion";
import { CmsModelFieldRendererPlugin, CmsModelFieldRendererProps } from "~/types";
import {
BindComponent,
BindComponentRenderProp,
CmsModel,
CmsModelField,
CmsModelFieldRendererPlugin,
CmsModelFieldRendererProps
} from "~/types";
import { SingleValueDynamicZone } from "./SingleValueDynamicZone";
import { MultiValueDynamicZone } from "./MultiValueDynamicZone";
import { FormElementMessage } from "@webiny/ui/FormElementMessage";
import { makeDecoratable } from "@webiny/react-composition";

const noBottomPadding = css`
> .webiny-ui-accordion-item__content {
padding-bottom: 0 !important;
}
`;

export type DynamicZoneContainerProps = {
field: CmsModelField;
getBind: (index?: number, key?: string) => BindComponent;
contentModel: CmsModel;
bind: BindComponentRenderProp;
children: React.ReactNode;
title?: string;
description?: string;
className?: string;
};

export const DynamicZoneContainer = makeDecoratable<
React.FunctionComponent<DynamicZoneContainerProps>
>("DynamicZoneContainer", props => {
const {
field,
bind: {
validation: { isValid, message }
},
title = field.label,
description = field.helpText,
className,
children
} = props;

const defaultClassName = field.multipleValues ? noBottomPadding : undefined;

return (
<>
<Accordion>
<AccordionItem
title={title}
description={description}
className={className || defaultClassName}
>
{children}
</AccordionItem>
</Accordion>
{isValid === false && <FormElementMessage error={true}>{message}</FormElementMessage>}
</>
);
});

const DynamicZoneContent = ({ field, getBind, contentModel }: CmsModelFieldRendererProps) => {
const templates = field.settings?.templates || [];
if (!templates.length) {
Expand All @@ -21,35 +72,27 @@ const DynamicZoneContent = ({ field, getBind, contentModel }: CmsModelFieldRende
return null;
}

const isMultipleValues = field.multipleValues === true;
const Bind = getBind();

const Component = isMultipleValues ? MultiValueDynamicZone : SingleValueDynamicZone;
const Component = field.multipleValues ? MultiValueDynamicZone : SingleValueDynamicZone;

return (
<Bind>
{bind => {
const { isValid, message } = bind.validation;
return (
<>
<Accordion>
<AccordionItem
title={field.label}
description={field.helpText}
className={isMultipleValues ? noBottomPadding : undefined}
>
<Component
bind={bind}
field={field}
getBind={getBind}
contentModel={contentModel}
/>
</AccordionItem>
</Accordion>
{isValid === false && (
<FormElementMessage error={true}>{message}</FormElementMessage>
)}
</>
<DynamicZoneContainer
field={field}
bind={bind}
getBind={getBind}
contentModel={contentModel}
>
<Component
bind={bind}
field={field}
getBind={getBind}
contentModel={contentModel}
/>
</DynamicZoneContainer>
);
}}
</Bind>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export { DynamicZoneContainerProps, DynamicZoneContainer } from "./dynamicZoneRenderer";
export {
MultiValueItemContainer,
MultiValueContainer,
MultiValueItem
} from "./MultiValueDynamicZone";
24 changes: 24 additions & 0 deletions packages/app-headless-cms/src/components.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import {
DynamicZoneContainer,
MultiValueContainer,
MultiValueItemContainer,
MultiValueItem
} from "~/admin/plugins/fieldRenderers/dynamicZone";

export const Components = {
FieldRenderers: {
DynamicZone: {
Container: DynamicZoneContainer,
// SingleValue: {
// Container: null,
// ItemContainer: null,
// Item: null
// },
MultiValue: {
Container: MultiValueContainer,
ItemContainer: MultiValueItemContainer,
Item: MultiValueItem
}
}
}
};
4 changes: 3 additions & 1 deletion packages/app-headless-cms/src/index.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import React from "react";
import { ContentEntryEditorConfig, ContentEntryListConfig } from "./admin/config/contentEntries";

export * from "./HeadlessCMS";
export * from "./admin/hooks";
export { LexicalEditorConfig } from "~/admin/lexicalConfig/LexicalEditorConfig";
export { RenderFieldElement } from "~/admin/components/ContentEntryForm/RenderFieldElement";
export { ModelProvider } from "~/admin/components/ModelProvider";
import { ContentEntryEditorConfig, ContentEntryListConfig } from "./admin/config/contentEntries";
export { ContentEntryEditorConfig, ContentEntryListConfig };

interface LegacyContentEntriesViewConfigProps {
Expand Down Expand Up @@ -34,3 +34,5 @@ export const ContentEntriesViewConfig = Object.assign(LegacyContentEntriesViewCo
Filter: ContentEntryListConfig.Browser.Filter,
Sorter: LegacySorter
});

export * from "./components";

0 comments on commit fb21e26

Please sign in to comment.