Skip to content

Commit

Permalink
Merge branch 'dev' into feature/2132-workflow-status
Browse files Browse the repository at this point in the history
  • Loading branch information
finnar-bin committed Feb 14, 2025
2 parents 2c5b620 + 3d6583c commit 40c5d17
Show file tree
Hide file tree
Showing 22 changed files with 348 additions and 293 deletions.
12 changes: 7 additions & 5 deletions cypress/e2e/schema/field.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ const SELECTORS = {
MAX_CHARACTER_LIMIT_INPUT: "MaxCharacterLimitInput",
MIN_CHARACTER_ERROR_MSG: "MinCharacterErrorMsg",
MAX_CHARACTER_ERROR_MSG: "MaxCharacterErrorMsg",
ADD_NEW_RELATED_ITEM: "add-relational-item-button",
CONFIRM_NEW_RELATED_ITEM: "done-selecting-item-button",
ACTIVE_RELATED_ITEM: "active-relational-item",
};

/**
Expand Down Expand Up @@ -341,13 +344,12 @@ describe("Schema: Fields", () => {
// click on the default value checkbox
cy.getBySelector(SELECTORS.DEFAULT_VALUE_CHECKBOX).click();
// enter a default value
cy.getBySelector(SELECTORS.DEFAULT_VALUE_INPUT).click();
cy.getBySelector(SELECTORS.ADD_NEW_RELATED_ITEM).click();
// Select the option
cy.get("[role=listbox] [role=option]").first().click();
cy.get(".MuiDataGrid-row").first().find("input").click();
cy.getBySelector(SELECTORS.CONFIRM_NEW_RELATED_ITEM).click();
// verify that the default value is set
cy.getBySelector(SELECTORS.DEFAULT_VALUE_INPUT)
.find("input")
.should("have.value", "- None -");
cy.getBySelector(SELECTORS.ACTIVE_RELATED_ITEM).should("have.length", 1);
cy.getBySelector(SELECTORS.DEFAULT_VALUE_CHECKBOX).click();

// Click done
Expand Down
65 changes: 42 additions & 23 deletions src/apps/content-editor/src/app/components/Editor/Field/Field.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ import {
import { ResolvedOption } from "./ResolvedOption";
import { LinkOption } from "./LinkOption";
import { FieldTypeMedia } from "../../FieldTypeMedia";
import { debounce } from "lodash";

const AIFieldShell = withAI(FieldShell);

Expand Down Expand Up @@ -203,6 +204,22 @@ export const Field = ({
const value = item?.data?.[name];
const version = item?.meta?.version;
const fieldData = fields?.find((field) => field.ZUID === ZUID);
const [inputValue, setInputValue] = useState(value || "");

const debouncedOnChange = useMemo(() => debounce(onChange, 300), [onChange]);

const deferredChange = useCallback(
(value, name) => {
setInputValue(value);
debouncedOnChange(value, name);
},
[debouncedOnChange]
);

// Keep local input value in sync with global field value
useEffect(() => {
setInputValue(value || "");
}, [value]);

useEffect(() => {
if (datatype !== "date" && datatype !== "datetime") {
Expand Down Expand Up @@ -288,7 +305,7 @@ export const Field = ({
ZUID={fieldData?.ZUID}
name={fieldData?.name || name}
label={fieldData?.label || label}
valueLength={(value as string)?.length ?? 0}
valueLength={(inputValue as string)?.length ?? 0}
settings={
fieldData || {
name: name,
Expand All @@ -304,11 +321,11 @@ export const Field = ({
minLength={minLength}
errors={errors}
aiType="text"
value={value}
value={inputValue}
>
<TextField
value={value}
onChange={(evt) => onChange(evt.target.value, name)}
value={inputValue}
onChange={(evt) => deferredChange(evt.target.value, name)}
fullWidth
inputProps={{
name: fieldData?.name || name,
Expand All @@ -322,12 +339,12 @@ export const Field = ({
return (
<FieldShell
settings={fieldData}
valueLength={(value as string)?.length ?? 0}
valueLength={(inputValue as string)?.length ?? 0}
errors={errors}
>
<TextField
value={value}
onChange={(evt) => onChange(evt.target.value, name)}
value={inputValue}
onChange={(evt) => deferredChange(evt.target.value, name)}
fullWidth
error={errors && Object.values(errors)?.some((error) => !!error)}
/>
Expand All @@ -338,14 +355,14 @@ export const Field = ({
return (
<FieldShell
settings={fieldData}
valueLength={(value as string)?.length ?? 0}
valueLength={(inputValue as string)?.length ?? 0}
errors={errors}
maxLength={maxLength}
withLengthCounter
>
<TextField
value={value}
onChange={(evt) => onChange(evt.target.value, name)}
value={inputValue}
onChange={(evt) => deferredChange(evt.target.value, name)}
fullWidth
type="url"
error={errors && Object.values(errors)?.some((error) => !!error)}
Expand Down Expand Up @@ -377,7 +394,7 @@ export const Field = ({
ZUID={fieldData?.ZUID}
name={fieldData?.name}
label={fieldData?.label}
valueLength={(value as string)?.length ?? 0}
valueLength={(inputValue as string)?.length ?? 0}
settings={fieldData}
onChange={(evt: ChangeEvent<HTMLInputElement>) =>
onChange(evt.target.value, name)
Expand All @@ -387,11 +404,11 @@ export const Field = ({
aiType="word"
maxLength={maxLength}
minLength={minLength}
value={value}
value={inputValue}
>
<TextField
value={value}
onChange={(evt) => onChange(evt.target.value, name)}
value={inputValue}
onChange={(evt) => deferredChange(evt.target.value, name)}
fullWidth
multiline
rows={6}
Expand Down Expand Up @@ -424,7 +441,7 @@ export const Field = ({
name={name}
value={value}
version={version}
onChange={onChange}
onChange={deferredChange}
onSave={onSave}
onCharacterCountChange={(charCount: number) =>
setCharacterCount(charCount)
Expand All @@ -448,22 +465,22 @@ export const Field = ({
ZUID={fieldData?.ZUID}
name={fieldData?.name}
label={fieldData?.label}
valueLength={(value as string)?.length ?? 0}
valueLength={(inputValue as string)?.length ?? 0}
settings={fieldData}
onChange={onChange}
errors={errors}
aiType="word"
datatype={fieldData?.datatype}
editorType={editorType}
onEditorChange={(value: EditorType) => setEditorType(value)}
value={value}
value={inputValue}
>
<FieldTypeEditor
// @ts-ignore component not typed
name={name}
value={value}
value={inputValue}
version={version}
onChange={onChange}
onChange={deferredChange}
datatype={datatype}
mediaBrowser={(opts: any) => {
setImageModal(opts);
Expand Down Expand Up @@ -789,6 +806,7 @@ export const Field = ({
relatedModelZUID={relatedModelZUID}
relatedFieldZUID={relatedFieldZUID}
onChange={onChange}
fieldLabel={fieldData?.label}
/>
{/**
<FieldTypeOneToOne
Expand Down Expand Up @@ -860,6 +878,7 @@ export const Field = ({
relatedModelZUID={relatedModelZUID}
relatedFieldZUID={relatedFieldZUID}
onChange={onChange}
fieldLabel={fieldData?.label}
/>
{/**
<FieldTypeOneToMany
Expand Down Expand Up @@ -918,10 +937,10 @@ export const Field = ({
return (
<FieldShell settings={fieldData} errors={errors}>
<FieldTypeNumber
value={+value || 0}
value={+inputValue || 0}
name={name}
required={required}
onChange={onChange}
onChange={deferredChange}
hasError={errors && Object.values(errors)?.some((error) => !!error)}
/>
</FieldShell>
Expand All @@ -937,8 +956,8 @@ export const Field = ({
<FieldTypeCurrency
name={name}
currency={settings?.currency ?? "USD"}
value={String(value)}
onChange={onChange}
value={String(inputValue)}
onChange={deferredChange}
error={errors && Object.values(errors)?.some((error) => !!error)}
/>
</FieldShell>
Expand Down
5 changes: 5 additions & 0 deletions src/apps/content-editor/src/app/views/ItemEdit/ItemEdit.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@ import { FreestyleWrapper } from "./FreestyleWrapper";
import { Meta } from "./Meta";
import { FieldError } from "../../components/Editor/FieldError";
import { AIGeneratorProvider } from "../../../../../../shell/components/withAi/AIGeneratorProvider";
import {
fetchItemPublishings,
fetchItems,
} from "../../../../../../shell/store/content";

const selectItemHeadTags = createSelector(
(state) => state.headTags,
Expand Down Expand Up @@ -416,6 +420,7 @@ export default function ItemEdit() {
} finally {
if (isMounted.current) {
setSaving(false);
dispatch(fetchItemPublishings());
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ export const ItemEditHeaderActions = ({
const [isConfirmPublishModalOpen, setIsConfirmPublishModalOpen] =
useState(false);
const [relatedItemsToPublish, setRelatedItemsToPublish] = useState<
ContentItem[]
ContentItemWithDirtyAndPublishing[]
>([]);
const [isPublishing, setIsPublishing] = useState(false);
const item = useSelector(
Expand Down Expand Up @@ -194,10 +194,11 @@ export const ItemEditHeaderActions = ({
if (!!item) {
const draftVersion = item?.meta?.version;
const publishedVersion = item?.publishing?.version || 0;
const scheduledVersion = item?.scheduling?.version || 0;

if (
draftVersion > publishedVersion &&
!item?.scheduling?.isScheduled
draftVersion > publishedVersion ||
scheduledVersion > publishedVersion
) {
return {
...item,
Expand Down Expand Up @@ -256,45 +257,60 @@ export const ItemEditHeaderActions = ({
const handlePublish = async () => {
if (allowPublish) {
setIsPublishing(true);
// If item is scheduled, delete the scheduled publishing first
if (itemState === ITEM_STATES.scheduled) {
await deleteItemPublishing({
modelZUID,
itemZUID,
publishingZUID: item?.scheduling?.ZUID,
});
}
try {
// Delete scheduled publishings first
const deleteScheduledPromises = [
// Delete main item's scheduled publishing if it exists
itemState === ITEM_STATES.scheduled &&
deleteItemPublishing({
modelZUID,
itemZUID,
publishingZUID: item?.scheduling?.ZUID,
}),
// Delete related items' scheduled publishings if they exist
...relatedItemsToPublish
.filter((item) => !!item.scheduling?.ZUID)
.map((item) =>
deleteItemPublishing({
modelZUID: item.meta.contentModelZUID,
itemZUID: item.meta.ZUID,
publishingZUID: item.scheduling.ZUID,
})
),
].filter((item) => !!item);

await Promise.all(deleteScheduledPromises);

Promise.allSettled([
createPublishing({
modelZUID,
itemZUID,
body: {
version: item?.meta.version,
publishAt: "now",
unpublishAt: "never",
},
}),
relatedItemsToPublish.map((item) => {
return createPublishing({
modelZUID: item.meta.contentModelZUID,
itemZUID: item.meta.ZUID,
// Proceed with publishing
await Promise.allSettled([
createPublishing({
modelZUID,
itemZUID,
body: {
version: item.meta.version,
version: item?.meta.version,
publishAt: "now",
unpublishAt: "never",
},
});
}),
])
.then(() => {
// Retain non rtk-query fetch of item publishing for legacy code
dispatch(fetchItemPublishings());
})
.finally(() => {
setIsPublishing(false);
setIsConfirmPublishModalOpen(false);
});
}),
...relatedItemsToPublish.map((item) =>
createPublishing({
modelZUID: item.meta.contentModelZUID,
itemZUID: item.meta.ZUID,
body: {
version: item.meta.version,
publishAt: "now",
unpublishAt: "never",
},
})
),
]);

// Retain non rtk-query fetch of item publishing for legacy code
dispatch(fetchItemPublishings());
} finally {
setIsPublishing(false);
setIsConfirmPublishModalOpen(false);
}
} else {
dispatch(
notify({
Expand Down Expand Up @@ -699,15 +715,15 @@ export const ItemEditHeaderActions = ({
<Typography variant="body2" fontWeight={600}>
Also publish related items
</Typography>
<Typography variant="body3">
<Typography variant="body3" color="text.secondary">
This will publish all items selected in the list below
</Typography>
<List disablePadding sx={{ mt: 1 }}>
{unpublishedRelatedItems.map((item, index) => (
<UnpublishedRelatedItem
key={item.meta.ZUID}
contentItem={item}
divider={unpublishedRelatedItems?.length > index + 1}
divider
selected={relatedItemsToPublish.some(
(i) => i.meta.ZUID === item.meta.ZUID
)}
Expand Down
Loading

0 comments on commit 40c5d17

Please sign in to comment.