Skip to content

Commit

Permalink
[SCSE-251] Refactor Web Structure (#83)
Browse files Browse the repository at this point in the history
* refactor done

* refactored blogs api + web layout
  • Loading branch information
xJQx authored Mar 19, 2023
1 parent d2c661d commit 63271ae
Show file tree
Hide file tree
Showing 49 changed files with 705 additions and 540 deletions.
8 changes: 8 additions & 0 deletions apps/web/.eslintrc.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
module.exports = {
root: true,
extends: ["custom"],
rules: {
"no-restricted-imports": [
"error",
{
"patterns": ["@features/*/*"]
}
]
},
};
4 changes: 2 additions & 2 deletions apps/web/__tests__/__snapshots__/index.snapshot.tsx.snap
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,10 @@ exports[`renders homepage unchanged 1`] = `
</div>
</div>
<div
class="chakra-stack css-1ma03zl"
class="chakra-stack css-12nxbtz"
>
<div
class="css-1by7d6v"
class="css-1jbryaa"
>
<div
class="css-0"
Expand Down
4 changes: 2 additions & 2 deletions apps/web/cypress/e2e/app.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ describe("Navigation", () => {
// The new url should include "/contact"
cy.url().should("include", "/contact");

// The new page should contain a heading with "Contact Us"
cy.get('[role="heading"]').contains("Contact Us");
// The new page should contain a heading with "CONTACT US"
cy.get('[role="heading"]').contains("CONTACT US");

// Find the first link with an href attribute containing "/" and click it
cy.get('a[href*="/"]').first().click();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { FooterContentText, FooterContentTextProps } from "ui";

export const AcademicsFooterContentText = () => {
const footerContentTextProps: FooterContentTextProps = {
announcements: [
{
title: "Update 16/12/2019: What we are doing",
description:
"Our committee aims to improve the quality of PYP and to build a network to help with the improvement of the PYP for years to come. " +
"Not only are we vetting through previous PYP solutions, we are creating a proper platform for students\n" +
"\n to inform us of any corrections/adjustments on the solutions. So be prepared!",
},
{
title: "Update 11/12/2019: Calling for Past Year Paper Solutions!",
description:
"Hope you are enjoying your holidays! We’re inviting students to write AY19/20 Semester 1 exam solutions (a.k.a PYP solutions). " +
"Your help will be greatly appreciated as the solution(s) will become valuable resources to many future exam\n candidates!",
},
],
};

return (
<FooterContentText {...footerContentTextProps} />
)
};
78 changes: 78 additions & 0 deletions apps/web/features/academics/components/PYPSection.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import { Box, Grid, GridItem, Heading, Text, VStack } from "@chakra-ui/react";
import { ButtonLink } from "ui";
import Image from "next/image";

export const PYPSection = () => {
const modules: Array<string> = [
"CE3006",
"CE3007",
"CE/CZ4022",
"CE/CZ4023",
"CE/CZ4064",
"CE/CZ4015",
];

return (
<VStack mx={{ base: 0, lg: 10 }} mb={12} p={5} spacing={5}>
<Heading p={5}>LINKS TO PYPs</Heading>

<Grid
templateColumns={{ base: "1fr", md: "repeat(2, 1fr)" }}
gap={5}
width={{ base: "100%", md: "auto" }}
>
<GridItem>
<ButtonLink
label="PYP QUESTIONS"
href="https://ts.ntu.edu.sg/sites/lib-repository/exam-question-papers/_layouts/15/start.aspx#/Shared%20Documents/Forms/AllItems.aspx"
width={{ base: "100%", md: "auto" }}
size="lg"
/>
</GridItem>
<GridItem>
<ButtonLink
label="PYP SOLUTIONS"
href="https://bit.ly/3CDVXlf"
variant="primary-black"
size="lg"
width={{ base: "100%", md: "auto" }}
/>
</GridItem>
</Grid>
<Grid
templateColumns={{ base: "1fr", md: "repeat(2, 1fr)" }}
gap={5}
width={{ base: "100%", md: "auto" }}
>
<GridItem justifySelf={{ base: "center", md: "flex-end" }}>
<Box>
<Image
src="/neve-web-design-studio-06.jpg"
alt="employees working in a studio"
width={530}
height={530}
/>
</Box>
</GridItem>
<GridItem maxWidth={530}>
<Text mb={30}>
To provide you with better support in the midst of the Covid-19
situation, we will be providing the PYP solutions online.
Moreover, we would like your support for the following modules:
</Text>
{modules.map((module) => (
<Text key={module} mb={30}>
{module}
</Text>
))}
<Text mb={30}>
Currently we are not able to provide the complete softcopy version
for those modules. If you possess a hardcopy of any of the
modules, please contact us for further details on how you can
help.
</Text>
</GridItem>
</Grid>
</VStack>
);
};
2 changes: 2 additions & 0 deletions apps/web/features/academics/components/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { PYPSection } from './PYPSection';
export { AcademicsFooterContentText } from './AcademicsFooterContentText';
2 changes: 2 additions & 0 deletions apps/web/features/academics/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// Export Components
export { PYPSection, AcademicsFooterContentText } from './components';
68 changes: 68 additions & 0 deletions apps/web/features/blogs/api/getAllBlogPosts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { GetAllBlogPostsResponse } from "@/lib/types/wordpress";
import { fetchAPI } from "@/lib/api/wordpress";
import { BlogFilterEnum } from "@/features/blogs";

interface IBlogFilter {
key: string;
category: BlogFilterEnum;
}

export async function getAllBlogPosts(filter?: IBlogFilter) {
const data = (await fetchAPI(`
query AllBlogPosts {
posts(first: 1000) {
edges {
node {
id
title
date
uri
slug
excerpt
author {
node {
id
name
}
}
featuredImage {
node {
link
}
}
}
}
}
}
`)) as GetAllBlogPostsResponse;

// Format Response
let posts = data?.posts.edges.map((edge) => ({
...edge,
node: {
...edge.node,
excerpt: edge.node.excerpt
.replace(/<[^>]+>/g, "")
.replace(/\n/g, " ")
.replace(/;&nbsp;/g, '"')
.substring(0, 120),
},
}))

// Filter posts (if any)
if (filter) {
switch (filter.category) {
case BlogFilterEnum.AUTHOR:
posts = posts.filter((edge) => {
if (edge.node.author) {
return edge.node.author.node.name === filter.key;
} else {
// no author
return !edge.node.author && filter.key === 'no-author';
}
})
}
}

return posts;
}
18 changes: 18 additions & 0 deletions apps/web/features/blogs/api/getAllBlogPostsSlugs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { GetAllBlogPostsResponse } from "@/lib/types/wordpress";
import { fetchAPI } from "@/lib/api/wordpress";

export async function getAllBlogPostsSlugs() {
const data = (await fetchAPI(`
query AllBlogPostsSlugs {
posts(first: 1000) {
edges {
node {
id
slug
}
}
}
}
`)) as GetAllBlogPostsResponse;
return data?.posts;
}
38 changes: 38 additions & 0 deletions apps/web/features/blogs/api/getBlogPost.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { GetBlogPostResponse } from "@/lib/types/wordpress";
import { fetchAPI } from "@/lib/api/wordpress";

export async function getBlogPost(postSlug: string) {
const data = (await fetchAPI(
`
query BlogPost($postSlug: ID!)
{
post(id: $postSlug, idType: SLUG) {
id
title
status
uri
slug
date
author {
node {
id
name
}
}
featuredImage {
node {
link
}
}
content
}
}
`,
{
variables: {
postSlug,
},
}
)) as GetBlogPostResponse;
return data?.post;
}
3 changes: 3 additions & 0 deletions apps/web/features/blogs/api/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export { getAllBlogPosts } from "./getAllBlogPosts";
export { getAllBlogPostsSlugs } from "./getAllBlogPostsSlugs";
export { getBlogPost } from "./getBlogPost";
40 changes: 40 additions & 0 deletions apps/web/features/blogs/components/BlogCardsDisplay.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { Grid, GridItem } from "@chakra-ui/react";
import { BlogCard } from "ui";
import { removeTextImgTag } from "@/lib/helpers/removeTextImgTag";
import { getDisplayDate } from "@/lib/helpers/getDisplayDate";
import { GetAllBlogPostsResponse } from "@/lib/types/wordpress";

export interface BlogCardsDisplayProps {
posts: GetAllBlogPostsResponse["posts"]["edges"];
}

export const BlogCardsDisplay = ({ posts }: BlogCardsDisplayProps) => {
return (
<Grid
templateColumns={{
base: "1fr",
md: "repeat(2, 1fr)",
xl: "repeat(3, 1fr)",
}}
gap={12}
pb={32}
>
{posts?.map((post) => (
<GridItem key={post.node.slug}>
<BlogCard
href={`blog/${post.node.slug}`}
blogCardImageProps={{
src: post.node.featuredImage?.node?.link ?? "",
alt: post.node.title,
}}
blogCardContentProps={{
title: post.node.title,
body: removeTextImgTag(post.node.excerpt) + "...",
date: getDisplayDate(new Date(post.node.date)),
}}
/>
</GridItem>
))}
</Grid>
);
};
71 changes: 71 additions & 0 deletions apps/web/features/blogs/components/BlogGridDisplay.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { Box, BoxProps, Grid, GridItem, Heading, Text } from "@chakra-ui/react";
import Link from "next/link";
import Image from "next/image";
import { getDisplayDate } from "@/lib/helpers/getDisplayDate";
import { GetAllBlogPostsResponse } from "@/lib/types/wordpress";

export interface BlogGridDisplayProps extends BoxProps {
posts: GetAllBlogPostsResponse["posts"]["edges"]
}

export const BlogGridDisplay = (props: BlogGridDisplayProps) => {
const { posts, ...boxProps } = props;

return (
<Box p="50px" display="flex" justifyContent="center" {...boxProps}>
<Grid
templateColumns={{ base: "1fr", lg: "0.5fr 1fr" }}
rowGap={{ base: "20px", lg: "75px" }}
columnGap="20px"
maxW={1100}
>
{posts?.map((post) => {
return (
<>
<GridItem
key={`blog-image-${post.node.id}`}
position="relative"
mb={{ base: 4, lg: 0 }}
>
{post.node.featuredImage && (
<Link href={`blog/${post.node.slug}`}>
<Image
src={post.node.featuredImage.node.link}
alt={post.node.title}
height={1000}
width={1000}
/>
</Link>
)}
</GridItem>
<GridItem
key={post.node.id + post.node.title}
display="flex"
flexDirection="column"
justifyContent="center"
mb={{ base: 6, lg: 0 }}
>
<Link href={`/blog/${post.node.slug}`}>
<Heading
size="lg"
mb="12px"
_hover={{ color: "gray.600", cursor: "pointer" }}
>
{post.node.title}
</Heading>
</Link>
<Text fontWeight="light" fontSize="13px">
by <Link href={`/blog/author/${post.node.author ? post.node.author.node.name : 'no-author'}`}><Text as="span" color="blue.700" fontWeight="bold" _hover={{ textDecoration: "underline" }}>{post.node.author?.node?.name}</Text></Link>
<Text as="span">{' '}/ {getDisplayDate(new Date(post.node.date))}</Text>
</Text>
<Text noOfLines={2} mt={2} alignSelf="start" fontSize="14px">
{post.node.excerpt + "..."}
</Text>
</GridItem>
</>
);
})}
</Grid>
</Box>
);
};
4 changes: 4 additions & 0 deletions apps/web/features/blogs/components/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export { BlogCardsDisplay } from './BlogCardsDisplay';
export type { BlogCardsDisplayProps } from './BlogCardsDisplay';
export { BlogGridDisplay } from './BlogGridDisplay';
export type { BlogGridDisplayProps } from './BlogGridDisplay';
Loading

1 comment on commit 63271ae

@vercel
Copy link

@vercel vercel bot commented on 63271ae Mar 19, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

website-ui-storybook – ./packages/ui

website-ui-storybook-git-main-cse-it.vercel.app
website-ui-storybook-cse-it.vercel.app
storybook.ui.dev.ntuscse.com

Please sign in to comment.