Skip to content

Commit

Permalink
Add raw ui for post page
Browse files Browse the repository at this point in the history
  • Loading branch information
mhevyk committed Sep 8, 2024
1 parent 2a88ab0 commit 9d063e3
Show file tree
Hide file tree
Showing 24 changed files with 613 additions and 71 deletions.
25 changes: 25 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
"@tinymce/tinymce-react": "^5.1.1",
"apollo-link-token-refresh": "^0.7.0",
"date-fns": "^3.6.0",
"dompurify": "^3.1.6",
"eventemitter3": "^5.0.1",
"formik": "^2.4.5",
"graphql": "^16.9.0",
Expand Down Expand Up @@ -60,6 +61,7 @@
"@testing-library/jest-dom": "^6.4.5",
"@testing-library/react": "^16.0.0",
"@trivago/prettier-plugin-sort-imports": "^4.3.0",
"@types/dompurify": "^3.0.5",
"@types/jest": "^29.5.12",
"@types/mapbox-gl": "^3.1.0",
"@types/react": "^18.2.15",
Expand Down
1 change: 1 addition & 0 deletions src/assets/icons/crown.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions src/assets/icons/heart.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions src/assets/icons/linkedin.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions src/assets/icons/share-link.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions src/assets/icons/three-dots-vertical.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions src/assets/icons/x-social.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
18 changes: 4 additions & 14 deletions src/layouts/blog-tab-layout/components/blog-tab/BlogTab.tsx
Original file line number Diff line number Diff line change
@@ -1,30 +1,20 @@
import { Link } from "react-router-dom";

import Box from "@mui/material/Box";

import { PostCategory } from "@/api/generated";
import useSearchParamsActions from "@/hooks/use-search-params-actions/useSearchParamsActions";
import { BlogTabButton } from "@/layouts/blog-tab-layout/components/blog-tab/BlogTab.styles";

type BlogTabProps = {
categoryLabel: PostCategory["name"];
categorySlug?: PostCategory["slug"];
};

export default function BlogTab({ categoryLabel, categorySlug }: BlogTabProps) {
const { setSearchParam, deleteSearchParam } = useSearchParamsActions();

const handleClick = () => {
if (categorySlug) {
setSearchParam("category", categorySlug);
} else {
deleteSearchParam("category");
}
};
const to = categorySlug ? `/posts?category=${categorySlug}` : "/posts";

return (
<Box component="li">
<BlogTabButton disableRipple disableTouchRipple onClick={handleClick}>
{categoryLabel}
</BlogTabButton>
<Link to={to}>{categoryLabel}</Link>
</Box>
);
}
25 changes: 25 additions & 0 deletions src/layouts/page-wrapper/PageWrapper.styles.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { styled } from "@mui/material";
import Container from "@mui/material/Container";

type ColoredWrapperProps = {
backgroundColor: string;
};

export const ColoredWrapper = styled("section", {
shouldForwardProp: prop => prop !== "backgroundColor",
})<ColoredWrapperProps>(props => ({
backgroundColor: props.backgroundColor,
padding: "100px 0",
}));

type ContentWrapperProps = {
backgroundColor: string;
};

export const ContentWrapper = styled(Container, {
shouldForwardProp: prop => prop !== "backgroundColor",
})<ContentWrapperProps>(props => ({
backgroundColor: props.backgroundColor,
maxWidth: "1058px !important",
padding: "20px !important",
}));
26 changes: 26 additions & 0 deletions src/layouts/page-wrapper/PageWrapper.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { PropsWithChildren } from "react";

import {
ColoredWrapper,
ContentWrapper,
} from "@/layouts/page-wrapper/PageWrapper.styles";
import theme from "@/theme/theme";

type PageWrapperProps = PropsWithChildren<{
wrapperBackgroundColor?: string;
containerBackgroundColor?: string;
}>;

export default function PageWrapper({
children,
wrapperBackgroundColor = theme.palette.CreamyDawn.main,
containerBackgroundColor = theme.palette.primary.main,
}: PageWrapperProps) {
return (
<ColoredWrapper backgroundColor={wrapperBackgroundColor}>
<ContentWrapper backgroundColor={containerBackgroundColor}>
{children}
</ContentWrapper>
</ColoredWrapper>
);
}
18 changes: 18 additions & 0 deletions src/pages/post/PostPage.styled.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { styled } from "@mui/material";
import Box from "@mui/material/Box";

import theme from "@/theme/theme";

export const BlogHeader = styled(Box)({
marginBottom: "60px",
});

export const PostWrapper = styled(Box)({
padding: "20px 100px 60px",
border: `1px solid ${theme.palette.grey[300]}`,
});

export const Title = styled("h2")({
...theme.typography.heading,
fontSize: "28px",
});
87 changes: 61 additions & 26 deletions src/pages/post/PostPage.tsx
Original file line number Diff line number Diff line change
@@ -1,38 +1,73 @@
// import { useParams } from "react-router-dom";
import { Navigate } from "react-router-dom";

import Box from "@mui/material/Box";
import Typography from "@mui/material/Typography";
import DOMPurify from "dompurify";

import "@/containers/post-editor/PostEditor.content.css";
import BlogTabLayout from "@/layouts/blog-tab-layout/BlogTabLayout";
import PageWrapper from "@/layouts/page-wrapper/PageWrapper";
import { BlogHeader, PostWrapper, Title } from "@/pages/post/PostPage.styled";
import PostFooter from "@/pages/post/components/post-footer/PostFooter";
import PostHeader from "@/pages/post/components/post-header/PostHeader";
import theme from "@/theme/theme";

// import { PostPageParams } from "@/types/helpers";

// TODO: replace with real data from backend
const title = "Why are Facials a Must for the Modern Woman";

const content = `<p>Create a blog post subtitle that summarizes your post in a few short, punchy sentences and entices your audience to continue reading.</p>
<p><strong><img style="display: block; margin-left: auto; margin-right: auto;" src="https://static.wixstatic.com/media/84770f_170242c7269d4ba2ba8ad50591e1a1e8~mv2_d_4500_2992_s_4_2.jpg/v1/fill/w_925,h_615,al_c,q_85,usm_0.66_1.00_0.01,enc_auto/84770f_170242c7269d4ba2ba8ad50591e1a1e8~mv2_d_4500_2992_s_4_2.jpg" width="668" height="444"></strong></p>
<p>Welcome to your blog post. Use this space to connect with your readers and potential customers in a way that&rsquo;s current and interesting. Think of it as an ongoing conversation where you can share updates about business, trends, news, and more.</p>
<blockquote>
<p><em>Do you have a design in mind for your blog? Whether you prefer a trendy postcard look or you&rsquo;re going for a more editorial style blog - there&rsquo;s a stunning layout for everyone.</em></p>
</blockquote>
<p>You&rsquo;ll be posting loads of engaging content, so be sure to keep your blog organized with Categories that also allow visitors to explore more of what interests them.</p>
<p><strong>Create Relevant Content</strong></p>
<p>&nbsp;</p>
<p>Writing a blog is a great way to position yourself as an authority in your field and captivate your readers&rsquo; attention. Do you want to improve your site&rsquo;s SEO ranking? Consider topics that focus on relevant keywords and relate back to your website or business. You can also add hashtags (<a class="QzAlv iqUF1" href="https://social-blog.wix.com/search/posts%3Fquery=%23vacation" target="_top" rel="noopener" data-hook="WebLink"><u>#vacation</u></a><u> </u><a class="QzAlv iqUF1" href="https://social-blog.wix.com/search/posts%3Fquery=%23dream" target="_top" rel="noopener" data-hook="WebLink"><u>#dream</u></a><u> </u><a class="QzAlv iqUF1" href="https://social-blog.wix.com/search/posts%3Fquery=%23summer" target="_top" rel="noopener" data-hook="WebLink"><u>#summer</u></a>) throughout your posts to reach more people, and help visitors search for relevant content. Blogging gives your site a voice, so let your business&rsquo; personality shine through. Choose a great image to feature in your post or add a video for extra engagement. Are you ready to get started? Simply create a new post now.</p>`;
// TODO: remove mock response and change type
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const mockPostResponse: { data: { post: any } } = {
data: {
post: {
author: {
id: 1,
username: "Admin",
role: "ADMIN",
},
createdAt: "2024-08-31T13:58:19.838Z",
estimatedReadTime: 1,
title: "Why are Facials a Must for the Modern Woman",
content:
'<p>Create a blog post subtitle that summarizes your post in a few short, punchy sentences and entices your audience to continue reading.</p>\n<p><strong><img style="display: block; margin-left: auto; margin-right: auto;" src="https://static.wixstatic.com/media/84770f_170242c7269d4ba2ba8ad50591e1a1e8~mv2_d_4500_2992_s_4_2.jpg/v1/fill/w_925,h_615,al_c,q_85,usm_0.66_1.00_0.01,enc_auto/84770f_170242c7269d4ba2ba8ad50591e1a1e8~mv2_d_4500_2992_s_4_2.jpg" width="668" height="444"></strong></p>\n<p>Welcome to your blog post. Use this space to connect with your readers and potential customers in a way that&rsquo;s current and interesting. Think of it as an ongoing conversation where you can share updates about business, trends, news, and more.</p>\n<blockquote>\n<p><em>Do you have a design in mind for your blog? Whether you prefer a trendy postcard look or you&rsquo;re going for a more editorial style blog - there&rsquo;s a stunning layout for everyone.</em></p>\n</blockquote>\n<p>You&rsquo;ll be posting loads of engaging content, so be sure to keep your blog organized with Categories that also allow visitors to explore more of what interests them.</p>\n<p><strong>Create Relevant Content</strong></p>\n<p>&nbsp;</p>\n<p>Writing a blog is a great way to position yourself as an authority in your field and captivate your readers&rsquo; attention. Do you want to improve your site&rsquo;s SEO ranking? Consider topics that focus on relevant keywords and relate back to your website or business. You can also add hashtags (<a class="QzAlv iqUF1" href="https://social-blog.wix.com/search/posts%3Fquery=%23vacation" target="_top" rel="noopener" data-hook="WebLink"><u>#vacation</u></a><u> </u><a class="QzAlv iqUF1" href="https://social-blog.wix.com/search/posts%3Fquery=%23dream" target="_top" rel="noopener" data-hook="WebLink"><u>#dream</u></a><u> </u><a class="QzAlv iqUF1" href="https://social-blog.wix.com/search/posts%3Fquery=%23summer" target="_top" rel="noopener" data-hook="WebLink"><u>#summer</u></a>) throughout your posts to reach more people, and help visitors search for relevant content. Blogging gives your site a voice, so let your business&rsquo; personality shine through. Choose a great image to feature in your post or add a video for extra engagement. Are you ready to get started? Simply create a new post now.</p>',
categories: [
{
name: "Daily Routine",
slug: "daily-routine",
},
{
name: "Herbs and Minerals",
slug: "herbs-and-minerals",
},
],
viewsCount: 0,
commentsCount: 0,
isLiked: false,
likesCount: 0,
},
},
};

// TODO: complete with real UI
export default function PostPage() {
// TODO: use param to fetch post from backend
// const params = useParams<PostPageParams>();
const { data } = mockPostResponse;

const post = data?.post;

if (!post) {
// TODO: improve not found page
return <Navigate to="/not-found" />;
}

const sanitizedContent = DOMPurify.sanitize(post.content);

return (
<Box>
<BlogTabLayout />
{/* TODO: change ui */}
<Typography>{title}</Typography>
{/* TODO: consider sanitizing */}
<Box dangerouslySetInnerHTML={{ __html: content }} />
</Box>
<PageWrapper wrapperBackgroundColor={theme.palette.PinkMarbleSky.main}>
<BlogHeader>
<BlogTabLayout />
</BlogHeader>
<PostWrapper>
<PostHeader post={post} />
<Title>{post.title}</Title>
<Box dangerouslySetInnerHTML={{ __html: sanitizedContent }} />
<PostFooter post={post} />
</PostWrapper>
</PageWrapper>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { ComponentType, SVGAttributes } from "react";

import CrownIcon from "@/assets/icons/crown.svg";
import UserIcon from "@/assets/icons/user-icon.svg";

import { USER_ROLES } from "@/constants";
import { UserRole } from "@/types/helpers";

export const postAuthorIconsByRole: Record<
UserRole,
ComponentType<SVGAttributes<SVGElement>>
> = {
[USER_ROLES.ADMIN]: CrownIcon,
[USER_ROLES.USER]: UserIcon,
[USER_ROLES.GUEST]: UserIcon,
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { styled } from "@mui/material";
import IconButton from "@mui/material/IconButton";

export const IconButtonStyled = styled(IconButton)({
padding: "2px",
});
49 changes: 49 additions & 0 deletions src/pages/post/components/author-role-button/AuthorRoleButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import Tooltip, { tooltipClasses } from "@mui/material/Tooltip";

import { postAuthorIconsByRole } from "@/pages/post/components/author-role-button/AuthorRoleButton.constants";
import { IconButtonStyled } from "@/pages/post/components/author-role-button/AuthorRoleButton.styles";
import theme from "@/theme/theme";
import { UserRole } from "@/types/helpers";

type AuthorRoleButtonProps = {
authorRole: UserRole;
};

export default function AuthorRoleButton({
authorRole,
}: AuthorRoleButtonProps) {
const PostAuthorRoleIcon = postAuthorIconsByRole[authorRole];

return (
<Tooltip
arrow
placement="top"
title={authorRole.toLowerCase()}
slotProps={{
tooltip: {
sx: {
backgroundColor: theme.palette.secondary.main,
},
},
arrow: {
sx: {
color: theme.palette.secondary.main,
},
},
popper: {
sx: {
textTransform: "capitalize",
[`&.${tooltipClasses.popper}[data-popper-placement*="top"] .${tooltipClasses.tooltip}`]:
{
marginBottom: "0px",
},
},
},
}}
>
<IconButtonStyled disableRipple>
<PostAuthorRoleIcon width="20px" height="20px" />
</IconButtonStyled>
</Tooltip>
);
}
Loading

0 comments on commit 9d063e3

Please sign in to comment.