Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
6 changes: 3 additions & 3 deletions cypress/e2e/seo/redirects/redirects.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,19 +20,19 @@ const TEST_REDIRECTS_DATA = [
path: "aaa/---test",
code: 301,
targetType: "page",
target: "All Field Types",
target: "7-b939a4-457q19",
},
{
path: "bbb/---test",
code: 301,
targetType: "page",
target: "All Field Types",
target: "7-b939a4-457q19",
},
{
path: "ccc/---test",
code: 301,
targetType: "external",
target: "All Field Types",
target: "7-b939a4-457q19",
},
{
path: "xxx/---test",
Expand Down
10 changes: 5 additions & 5 deletions etc/nginx.conf
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,11 @@ http {
add_header X-Permitted-Cross-Domain-Policies "";
add_header X-Frame-Options "deny";
add_header Referrer-Policy "no-referrer-when-downgrade";
add_header Content-Security-Policy "connect-src 'self' *.amplitude.com *.zesty.io *.a.run.app *.tiny.cloud *.getbynder.com *.bynder.com d8ejoa1fys2rk.cloudfront.net *.sentry.io www.googleapis.com us-central1-zesty-dev.cloudfunctions.net us-central1-zesty-stage.cloudfunctions.net us-central1-zesty-prod.cloudfunctions.net;";
# *.a.run.app - zesty cloudrun apps
# d8ejoa1fys2rk.cloudfront.net - bynder modules
# googleapis.com - google fonts
# us-central1-zesty-dev.cloudfunctions.net - zesty cloud functions
add_header Content-Security-Policy "connect-src 'self' *.zesty.io *.a.run.app us-central1-zesty-dev.cloudfunctions.net us-central1-zesty-stage.cloudfunctions.net us-central1-zesty-prod.cloudfunctions.net *.sentry.io www.googleapis.com *.amplitude.com *.tiny.cloud *.getbynder.com *.bynder.com d8ejoa1fys2rk.cloudfront.net dam.redshieldtoolkit.org brand.frontdoor.com;";
# *.a.run.app - zesty cloudrun apps
# d8ejoa1fys2rk.cloudfront.net - bynder modules
# googleapis.com - google fonts
# us-central1-zesty-dev.cloudfunctions.net - zesty cloud functions
add_header Feature-Policy "";
}

Expand Down
151 changes: 89 additions & 62 deletions src/apps/content-editor/src/app/components/FieldTypeMedia.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import {
import React, {
useCallback,
useEffect,
useMemo,
useState,
useImperativeHandle,
forwardRef,
useRef,
} from "react";
import { useDispatch, useSelector } from "react-redux";
import {
Expand Down Expand Up @@ -41,7 +42,7 @@ import {
} from "../../../../../shell/services/mediaManager";
import { fileUploadStage } from "../../../../../shell/store/media-revamp";
import { useDropzone } from "react-dropzone";
import { IconButton, ImageSync, theme } from "@zesty-io/material";
import { IconButton, ImageSync } from "@zesty-io/material";
import { FileModal } from "../../../../media/src/app/components/FileModal";
import RenameFileModal from "../../../../media/src/app/components/FileModal/RenameFileModal";
import { fileExtension } from "../../../../media/src/app/utils/fileUtils";
Expand All @@ -51,7 +52,8 @@ import { FileTypePreview } from "../../../../media/src/app/components/FileModal/
import { useGetInstanceSettingsQuery } from "../../../../../shell/services/instance";
import { ReplaceFileModal } from "../../../../media/src/app/components/FileModal/ReplaceFileModal";
import openBynder from "../../../../../utility/openBynder";

import { useDrag, useDrop } from "react-dnd";
import DndContextProvider from "shell/components/DndContextProvider";
type FieldTypeMediaProps = {
images: string[];
limit: number;
Expand Down Expand Up @@ -491,28 +493,30 @@ export const FieldTypeMedia = forwardRef(
hasError ? `1px solid ${theme.palette.error.main}` : "none",
}}
>
{sortedImages.map((image, index) => {
const isBynderAsset = image.includes("bynder.com");

return (
<MediaItem
key={image}
imageZUID={image}
index={index}
setDraggedIndex={setDraggedIndex}
setHoveredIndex={setHoveredIndex}
onReorder={handleReorder}
onPreview={(imageZUID: string) => setShowFileModal(imageZUID)}
onRemove={removeImage}
onReplace={(imageZUID) => {
setImageToReplace(imageZUID);
}}
hideDrag={hideDrag || limit === 1}
isBynderAsset={isBynderAsset}
isBynderSessionValid={!!isBynderSessionValid}
/>
);
})}
<DndContextProvider>
{sortedImages.map((image, index) => {
const isBynderAsset = image?.includes("bynder.com");

return (
<MediaItem
key={image}
imageZUID={image}
index={index}
setDraggedIndex={setDraggedIndex}
setHoveredIndex={setHoveredIndex}
onReorder={handleReorder}
onPreview={(imageZUID: string) => setShowFileModal(imageZUID)}
onRemove={removeImage}
onReplace={(imageZUID) => {
setImageToReplace(imageZUID);
}}
hideDrag={hideDrag || limit === 1}
isBynderAsset={isBynderAsset}
isBynderSessionValid={!!isBynderSessionValid}
/>
);
})}
</DndContextProvider>
{limit > images.length && (
<Box display="flex" gap={1}>
{!isBynderSessionValid && (
Expand Down Expand Up @@ -608,7 +612,7 @@ export const MediaItem = ({
isBynderSessionValid,
hideActionButtons,
}: MediaItemProps) => {
const [isDragging, setIsDragging] = useState(false);
const lastHoveredIndexRef = useRef(null);
const [isDraggable, setIsDraggable] = useState(false);
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
const { data, isFetching } = useGetFileQuery(imageZUID, {
Expand Down Expand Up @@ -658,29 +662,7 @@ export const MediaItem = ({
});
};

const isURL = imageZUID.substr(0, 4) === "http";

const handleDragStart = (e: React.DragEvent) => {
setIsDragging(true);
setDraggedIndex(index);
};

const handleDrag = (e: React.DragEvent) => {
e.preventDefault();
};

const handleDragEnd = () => {
setIsDragging(false);
onReorder();
};

const handleDragEnter = (e: React.DragEvent) => {
setHoveredIndex(index);
};

const handleDragOver = (e: React.DragEvent) => {
e.preventDefault();
};
const isURL = imageZUID?.substr(0, 4) === "http";

const handleUpdateMutation = (renamedFilename?: string) => {
let constructedFileType = "";
Expand All @@ -697,20 +679,66 @@ export const MediaItem = ({
});
};

const [{ isDragging }, drag, preview] = hideDrag
? [{ isDragging: false }, null, null]
: useDrag(
{
type: "FIELD_TYPE_MEDIA",
item: () => {
setDraggedIndex?.(index);
return { index, imageZUID };
},
collect: (monitor) => ({
isDragging: monitor.isDragging(),
}),
end: () => {
onReorder?.();
lastHoveredIndexRef.current = null;
},
},
[index, imageZUID, onReorder, setDraggedIndex]
);

const [, drop] = hideDrag
? [, null]
: useDrop(
{
accept: "FIELD_TYPE_MEDIA",
hover: (item: { index: number; imageZUID: string }, monitor) => {
if (
!monitor.isOver({ shallow: true }) ||
lastHoveredIndexRef.current === index
) {
return;
}
setHoveredIndex?.(index);
lastHoveredIndexRef.current = index;
},
},
[index, setHoveredIndex]
);

const dragDropRef = useCallback(
(node: HTMLElement | null) => {
if (hideDrag) return;

drag(node);
drop(node);
preview(node);
},
[drag, drop, preview, hideDrag]
);

return (
<>
<Box
ref={hideDrag ? null : dragDropRef}
data-cy="mediaItem"
display="grid"
gridTemplateColumns={
hideDrag ? "min-content 1fr" : "repeat(2, min-content) 1fr"
}
draggable={isDraggable}
onDragStart={handleDragStart}
onDrag={handleDrag}
onDragEnter={handleDragEnter}
onDragEnd={handleDragEnd}
onDragOver={handleDragOver}
onClick={() => {
if (isURL || !data) return;

Expand All @@ -719,15 +747,11 @@ export const MediaItem = ({
alignItems="center"
sx={{
border: (theme) => `1px solid ${theme.palette.border}`,
borderRadius: "8px",
"&:hover": {
backgroundColor: "action.hover",
cursor: "pointer",
},
borderRadius: 2,
backgroundColor: "background.paper",
...(isDragging && {
opacity: 0.01,
}),
overflow: "hidden",
opacity: isDragging ? 0 : 1,
transform: "translate(0, 0)",
}}
position="relative"
>
Expand Down Expand Up @@ -757,7 +781,10 @@ export const MediaItem = ({
)}
{!hideDrag && (
<IconButton
ref={!!hideDrag ? null : drag}
disableRipple
disableFocusRipple
disableTouchRipple
size="small"
sx={{
cursor: "grab",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import {
import { FieldWrapper } from "../../../../../seo/src/app/components/RedirectsDialogProvider/CreateRedirects/CreateForm";
import SearchField from "../../../../../seo/src/app/components/RedirectsDialogProvider/CreateRedirects/SearchField";
import PathField from "../../../../../seo/src/app/components/RedirectsDialogProvider/CreateRedirects/PathField";
import { searchItems } from "shell/store/content";
export type ContentRedirectModalProps = {
open: boolean;
onClose: () => void;
Expand Down Expand Up @@ -114,6 +115,10 @@ export const ContentRedirectModal: FC<ContentRedirectModalProps> = ({
return isValidUrl;
};

const handleSearch = (term: string) => {
dispatch(searchItems(term));
};

return (
<Dialog
open={open}
Expand Down Expand Up @@ -240,6 +245,7 @@ export const ContentRedirectModal: FC<ContentRedirectModalProps> = ({
value={targetInternal}
defaultValue={targetPath}
onChange={setTargetInternal}
onSearch={handleSearch}
/>
) : (
<PathField
Expand Down
38 changes: 34 additions & 4 deletions src/apps/content-editor/src/app/views/Redirects/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,14 @@ import { AppState } from "../../../../../../shell/store/types";
import { useParams } from "react-router";
import { useDomain } from "../../../../../../shell/hooks/use-domain";
import { DeleteRedirectModal } from "./DeleteRedirectModal";
import { RedirectsTargetType } from "../../../../../../shell/services/types";
import {
ContentItemWithDirtyAndPublishing,
RedirectsTargetType,
} from "../../../../../../shell/services/types";
import { useRedirectsDialog } from "../../../../../seo/src/app/components/RedirectsDialogProvider";
import AddIcon from "@mui/icons-material/Add";
import AutoSizer from "react-virtualized-auto-sizer";
import ContentRedirects, { ContentRedirectsSkeleton } from "./ContentRedirects";
import { useContentItems } from "../../../../../seo/src/app/components/RedirectsDialogProvider/useContentItems";

type Row = {
id: string;
Expand All @@ -51,10 +53,38 @@ export const Redirects = () => {
isLoading: isLoadingRedirects,
isFetching: isFetchingRedirects,
} = useGetRedirectsQuery();
const { options, isLoading: isLoadingOptions } = useContentItems();

const { web } = useSelector((state: AppState) => state.content[itemZUID]);
const contentItems = useSelector((state: AppState) => state.content);
const contentModels = useSelector((state: AppState) => state.models);
const languages = useSelector((state: any) => state.languages);

const options = useMemo(() => {
return Object.values(contentItems)
.filter((item) => item?.meta?.ZUID && item?.web?.path)
.sort((a, b) => {
const dateA = new Date(a.meta.createdAt).getTime();
const dateB = new Date(b.meta.createdAt).getTime();
return dateB - dateA;
})
.map((item: ContentItemWithDirtyAndPublishing) => {
const web = item.web;
const meta = item.meta;
const publishing = item.publishing;
return {
ZUID: meta?.ZUID,
label: web?.metaTitle || web?.metaLinkText || web?.path || "",
langCode:
languages.find((lang: any) => lang.ID === meta?.langID)?.code ||
"en-US",
path: web?.path,
type: contentModels[meta?.contentModelZUID]?.type || "",
isPublished: publishing?.isPublished || false,
};
});
}, [contentItems, contentModels, languages]);

const isLoading = isLoadingRedirects || isLoadingOptions;
const isLoading = isLoadingRedirects;

const redirectsHere = useMemo(() => {
if (!redirects?.length || !web?.path) return [];
Expand Down
Loading
Loading