Skip to content
Merged
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
5 changes: 1 addition & 4 deletions cypress/e2e/blocks/tests/sidebar.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,7 @@ describe("Blocks sidebar", () => {

it("can navigate blocks in the sidebar", () => {
cy.contains("Test Block Do Not Delete").click();
cy.location("pathname").should(
"eq",
"/blocks/6-d8b088cc9c-gwk3w7/7-ee94b5e98d-ss015b"
);
cy.location("pathname").should("eq", "/blocks/6-d8b088cc9c-gwk3w7");
});

it("should be able to show the more options menu", () => {
Expand Down
210 changes: 164 additions & 46 deletions src/apps/blocks/views/BlockModel.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
import { useEffect, useState } from "react";
import { useHistory, useParams } from "react-router";
import { Box, CircularProgress, Typography, Button } from "@mui/material";
import {
Box,
CircularProgress,
Typography,
Button,
TextField,
InputAdornment,
} from "@mui/material";
import AddRoundedIcon from "@mui/icons-material/AddRounded";
import SearchRoundedIcon from "@mui/icons-material/SearchRounded";
import {
useGetContentModelItemsQuery,
useGetContentModelsQuery,
Expand All @@ -12,22 +20,22 @@ import { fetchModels } from "../../../shell/store/models";
import { useDispatch } from "react-redux";
import { fetchFields } from "../../../shell/store/fields";
import { useParams as useSearchParams } from "../../../shell/hooks/useParams";
import { ContentItem } from "shell/services/types";
import SearchBox from "shell/components/SearchBox";
import blockPlaceholder from "../../../../public/images/blockPlaceholder.png";

export const BlockModel = () => {
const dispatch = useDispatch();
const history = useHistory();
const { modelZUID } = useParams<{
modelZUID: string;
}>();
const history = useHistory();
const [params] = useSearchParams();
const [renderFlag, setRenderFlag] = useState(false);
const [showCreateVariantDialog, setShowCreateVariantDialog] = useState(false);
const { data, isFetching, error, isUninitialized } =
const [search, setSearch] = useState("");
const { data, isLoading, error, isUninitialized } =
useGetContentModelItemsQuery({
modelZUID,
params: {
limit: 1,
},
});

const { data: models } = useGetContentModelsQuery();
Expand All @@ -38,46 +46,110 @@ export const BlockModel = () => {
dispatch(fetchFields(modelZUID));
}, []);

useEffect(() => {
if (data?.length && !isFetching && !error) {
history.push(`/blocks/${modelZUID}/${data?.[0]?.meta?.ZUID}`);
}

if (!isFetching && !isUninitialized && !data?.length) {
setRenderFlag(true);
}
}, [data, isFetching, error, history]);

useEffect(() => {
setShowCreateVariantDialog(!!params?.get("createVariant"));
}, [params]);

const model = models?.find((model) => model.ZUID === modelZUID);

if (renderFlag) {
if (isLoading || isUninitialized) {
return (
<>
<Box
display="flex"
justifyContent="center"
alignItems="center"
height="100%"
width="100%"
>
<CircularProgress />
</Box>
);
}

return (
<>
<Box
width="100%"
display="flex"
flexDirection="column"
sx={{ backgroundColor: "grey.50" }}
>
<Box
width="100%"
display="flex"
flexDirection="column"
sx={{ backgroundColor: "grey.50" }}
justifyContent="space-between"
alignItems="center"
px={4}
pt={4}
pb={1.75}
sx={{
borderBottom: (theme) => `2px solid ${theme.palette.border}`,
backgroundColor: "background.paper",
}}
>
<Typography variant="h3" fontWeight="700">
{model?.label}
</Typography>
<Box display={"flex"} alignItems="center" gap={2}>
<SearchBox
data-cy="search-blocks-input"
value={search}
onChange={(event) => setSearch(event.target.value)}
size="small"
sx={{
width: "240px",
"& .MuiOutlinedInput-notchedOutline": {
border: 0,
},
}}
InputProps={{
sx: {
backgroundColor: "grey.50",
},
startAdornment: (
<InputAdornment position="start">
<SearchRoundedIcon fontSize="small" color="action" />
</InputAdornment>
),
}}
placeholder="Search Variants"
/>
<Button
variant="contained"
size="small"
startIcon={<AddRoundedIcon />}
onClick={() => setShowCreateVariantDialog(true)}
data-cy="create-variant-button-header"
>
Create Variant
</Button>
</Box>
</Box>
{data?.length ? (
<Box
px={4.5}
py={4}
display="flex"
justifyContent="space-between"
px={4}
pt={4}
pb={1.75}
gap={2}
flexWrap={"wrap"}
sx={{
borderBottom: (theme) => `2px solid ${theme.palette.border}`,
backgroundColor: "background.paper",
overflowY: "scroll",
}}
>
<Typography variant="h3" fontWeight="700">
{model?.label}
</Typography>
{data
?.filter?.((item) =>
item.web.metaTitle.toLowerCase().includes(search.toLowerCase())
)
?.map((item) => (
<BlockVariantCard
key={item.meta.ZUID}
item={item}
onClick={() => {
history.push(`/blocks/${modelZUID}/${item.meta.ZUID}`);
}}
/>
))}
</Box>
) : (
<Box
display="flex"
height="100%"
Expand Down Expand Up @@ -112,26 +184,72 @@ export const BlockModel = () => {
/>
</Box>
</Box>
</Box>
{showCreateVariantDialog && (
<CreateVariantDialog
onClose={() => setShowCreateVariantDialog(false)}
model={model}
/>
)}
</>
);
}
</Box>
{showCreateVariantDialog && (
<CreateVariantDialog
onClose={() => setShowCreateVariantDialog(false)}
model={model}
/>
)}
</>
);
};

type BlockVariantCardProps = {
item: ContentItem;
onClick: () => void;
};

const BlockVariantCard = ({ item, onClick }: BlockVariantCardProps) => {
const [noImage, setNoImage] = useState(false);
return (
<Box
display="flex"
justifyContent="center"
alignItems="center"
height="100%"
width="100%"
key={item.meta.ZUID}
width={265}
sx={{
border: (theme) => `1px solid ${theme.palette.border}`,
boxSizing: "border-box",
cursor: "pointer",
}}
onClick={onClick}
>
<CircularProgress />
<Box px={1} pt={1} pb={1.75}>
{!noImage ? (
<Box
component="img"
src={item.data.og_image as string}
minHeight={146}
maxHeight={146}
width="100%"
sx={{ objectFit: "contain" }}
onError={(e: any) => {
setNoImage(true);
}}
/>
) : (
<Box
minHeight={146}
maxHeight={146}
width="100%"
component="img"
sx={{ objectFit: "contain" }}
src={blockPlaceholder}
/>
)}
</Box>
<Box
py={2}
px={1}
sx={{
backgroundColor: "background.paper",
}}
height={52}
>
<Typography noWrap variant="body2">
{item.web.metaTitle}
</Typography>
</Box>
</Box>
);
};
62 changes: 41 additions & 21 deletions src/apps/content-editor/src/app/components/ContentBreadcrumbs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { ContentNavItem } from "../../../../../shell/services/types";
import { MODEL_ICON } from "../../../../../shell/constants";
import { Link } from "react-router-dom";
import { CustomBreadcrumbs } from "../../../../../shell/components/CustomBreadcrumbs";
import { useSelector } from "react-redux";

export const ContentBreadcrumbs = () => {
const { data: nav, isLoading: isNavLoading } = useGetContentNavItemsQuery();
Expand All @@ -26,6 +27,8 @@ export const ContentBreadcrumbs = () => {
}>();
const history = useHistory();
const location = useLocation();
const models = useSelector((state: any) => state.models);
const isInBlocksView = location?.pathname?.includes("/blocks/");

const breadcrumbData = useMemo(() => {
const isInMultipageTableView = !["new", "import"].includes(
Expand All @@ -48,6 +51,17 @@ export const ContentBreadcrumbs = () => {
activeItem = nav?.find((item) => item.ZUID === modelZUID);
if (activeItem) {
crumbs.push(activeItem);
} else if (models[modelZUID]?.type === "block") {
const model = models[modelZUID];
activeItem = {
ZUID: model.ZUID,
contentModelZUID: model.ZUID,
label: model.label,
parentZUID: model.parentZUID,
sort: model.sort,
type: model.type,
};
crumbs.push(activeItem);
} else {
return [];
}
Expand All @@ -74,6 +88,8 @@ export const ContentBreadcrumbs = () => {
onClick: () => {
if (item.type === "item") {
history.push(`/content/${item.contentModelZUID}/${item.ZUID}`);
} else if (item.type === "block") {
history.push(`/blocks/${item.ZUID}`);
} else {
history.push(`/content/${item.contentModelZUID}`);
}
Expand Down Expand Up @@ -129,27 +145,31 @@ export const ContentBreadcrumbs = () => {
return (
<CustomBreadcrumbs
items={[
{
node: (
<Link
style={{
display: "flex",
}}
to={`/content/${
nav?.find((item) => item.label === "Homepage")?.contentModelZUID
}/${nav?.find((item) => item.label === "Homepage")?.ZUID}`}
>
<Home color="action" fontSize="small" />
</Link>
),
onClick: () => {
history.push(
`/content/${
nav?.find((item) => item.label === "Homepage")?.contentModelZUID
}/${nav?.find((item) => item.label === "Homepage")?.ZUID}`
);
},
},
...(!isInBlocksView
? [
{
node: (
<Link
style={{ display: "flex" }}
to={`/content/${
nav?.find((item) => item.label === "Homepage")
?.contentModelZUID
}/${nav?.find((item) => item.label === "Homepage")?.ZUID}`}
>
<Home color="action" fontSize="small" />
</Link>
),
onClick: () => {
history.push(
`/content/${
nav?.find((item) => item.label === "Homepage")
?.contentModelZUID
}/${nav?.find((item) => item.label === "Homepage")?.ZUID}`
);
},
},
]
: []),
...breadcrumbData,
]}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ export const ItemEditHeader = ({
>
<Box display="flex" justifyContent="space-between" gap={4}>
<Box>
{type !== "block" && <ContentBreadcrumbs />}
<ContentBreadcrumbs />
{isLoadingItem &&
(!modelLabel || !item || !Object.keys(item?.web).length) ? (
<Stack>
Expand Down
3 changes: 2 additions & 1 deletion src/shell/services/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -445,7 +445,8 @@ type ContentNavItemType =
| "internal"
| "item"
| "pageset"
| "redirect";
| "redirect"
| "block";

export interface ContentNavItem {
ZUID: string;
Expand Down
Loading