From 857b12e00b86ada25ef084a5ac839d08cb5b05ab Mon Sep 17 00:00:00 2001 From: Nalin Date: Fri, 28 Feb 2025 18:17:32 +0530 Subject: [PATCH 01/24] Added load more button in /blog endpoint, as it was asked in Design Audit --- README.md | 1 + package-lock.json | 9 +++--- package.json | 2 +- pages/blog/index.tsx | 73 ++++++++++++++++++++++++++------------------ 4 files changed, 51 insertions(+), 34 deletions(-) diff --git a/README.md b/README.md index 5f153826f3c7..e873caa0bfcf 100644 --- a/README.md +++ b/README.md @@ -406,6 +406,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d Raj Patel
Raj Patel

💻 + Nalin Dalal
Nalin Dalal

💻 diff --git a/package-lock.json b/package-lock.json index 046d3f1e62fc..182a974efc39 100644 --- a/package-lock.json +++ b/package-lock.json @@ -55,7 +55,6 @@ "node-fetch": "^3.3.2", "node-fetch-2": "npm:node-fetch@^2.7.0", "postcss": "^8.4.35", - "prettier": "^3.3.3", "react": "^18", "react-dom": "^18", "react-ga": "^3.3.1", @@ -119,6 +118,7 @@ "inquirer": "^9.2.14", "jest": "^29.7.0", "postcss-import": "^16.0.1", + "prettier": "^3.5.2", "remark-cli": "^12.0.1", "remark-lint": "^10.0.0", "remark-mdx": "^3.0.1", @@ -24204,9 +24204,10 @@ } }, "node_modules/prettier": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz", - "integrity": "sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.2.tgz", + "integrity": "sha512-lc6npv5PH7hVqozBR7lkBNOGXV9vMwROAPlumdBkX0wTbbzPu/U1hk5yL8p2pt4Xoc+2mkT8t/sow2YrV/M5qg==", + "license": "MIT", "bin": { "prettier": "bin/prettier.cjs" }, diff --git a/package.json b/package.json index 22c6192d66fa..f4a3da00e12d 100644 --- a/package.json +++ b/package.json @@ -92,7 +92,6 @@ "node-fetch": "^3.3.2", "node-fetch-2": "npm:node-fetch@^2.7.0", "postcss": "^8.4.35", - "prettier": "^3.3.3", "react": "^18", "react-dom": "^18", "react-ga": "^3.3.1", @@ -156,6 +155,7 @@ "inquirer": "^9.2.14", "jest": "^29.7.0", "postcss-import": "^16.0.1", + "prettier": "^3.5.2", "remark-cli": "^12.0.1", "remark-lint": "^10.0.0", "remark-mdx": "^3.0.1", diff --git a/pages/blog/index.tsx b/pages/blog/index.tsx index b6a13e507157..1e880f82d18f 100644 --- a/pages/blog/index.tsx +++ b/pages/blog/index.tsx @@ -21,48 +21,52 @@ export default function BlogIndexPage() { const router = useRouter(); const { navItems } = useContext(BlogContext); - const [posts, setPosts] = useState( - navItems - ? navItems.sort((i1: IBlogPost, i2: IBlogPost) => { - const i1Date = new Date(i1.date); - const i2Date = new Date(i2.date); + const [posts, setPosts] = useState([]); + const [isClient, setIsClient] = useState(false); + const [visiblePosts, setVisiblePosts] = useState(9); // Number of posts initially visible - if (i1.featured && !i2.featured) return -1; - if (!i1.featured && i2.featured) return 1; + useEffect(() => { + if (navItems) { + const sortedPosts = navItems.sort((i1: IBlogPost, i2: IBlogPost) => { + const i1Date = new Date(i1.date); + const i2Date = new Date(i2.date); - return i2Date.getTime() - i1Date.getTime(); - }) - : [] - ); - const [isClient, setIsClient] = useState(false); + if (i1.featured && !i2.featured) return -1; + if (!i1.featured && i2.featured) return 1; + + return i2Date.getTime() - i1Date.getTime(); + }); - const onFilter = (data: IBlogPost[]) => setPosts(data); - const toFilter = [ - { - name: 'type' - }, - { - name: 'authors', - unique: 'name' - }, - { - name: 'tags' + setPosts(sortedPosts); } - ]; + }, [navItems]); + + useEffect(() => { + setIsClient(true); + }, []); + + const loadMore = () => { + setVisiblePosts((prev) => prev + 9); // Load additional 9 posts + }; + + const onFilter = (data: IBlogPost[]) => { + setPosts(data); + setVisiblePosts(9); // Reset visible posts on filter change + }; + + const toFilter = [{ name: 'type' }, { name: 'authors', unique: 'name' }, { name: 'tags' }]; + const clearFilters = () => { router.push(`${router.pathname}`, undefined, { shallow: true }); }; + const showClearFilters = Object.keys(router.query).length > 0; const description = 'Find the latest and greatest stories from our community'; const image = '/img/social/blog.webp'; - useEffect(() => { - setIsClient(true); - }, []); - return (
@@ -122,7 +126,7 @@ export default function BlogIndexPage() { )} {Object.keys(posts).length > 0 && isClient && (
    - {posts.map((post, index) => ( + {posts.slice(0, visiblePosts).map((post, index) => ( ))}
@@ -132,6 +136,17 @@ export default function BlogIndexPage() {
)} + {visiblePosts < posts.length && isClient && ( +
+ +
+ )} From 8d9e44407c9383c8b51ffbe272e8bd71071793fd Mon Sep 17 00:00:00 2001 From: Nalin Date: Sat, 1 Mar 2025 19:17:17 +0530 Subject: [PATCH 02/24] Added email validation, it was literally taking anything,check if the email follows a valid pattern using a regular expression (regex --- components/NewsletterSubscribe.tsx | 78 ++++++++++++++++++++++-------- 1 file changed, 57 insertions(+), 21 deletions(-) diff --git a/components/NewsletterSubscribe.tsx b/components/NewsletterSubscribe.tsx index 3d9e0863dd69..52ef4559d0e0 100644 --- a/components/NewsletterSubscribe.tsx +++ b/components/NewsletterSubscribe.tsx @@ -17,7 +17,7 @@ enum FormStatus { NORMAL = 'normal', LOADING = 'loading', SUCCESS = 'success', - ERROR = 'error' + ERROR = 'error', } interface NewsletterSubscribeProps { @@ -46,7 +46,7 @@ export default function NewsletterSubscribe({ title = 'Subscribe to our newsletter to receive news about AsyncAPI.', subtitle = 'We respect your inbox. No spam, promise ✌️', type = 'Newsletter', - errorSubtitle = 'Subscription failed, please let us know about it by submitting a bug' + errorSubtitle = 'Subscription failed, please let us know about it by submitting a bug', }: NewsletterSubscribeProps) { const [email, setEmail] = useState(''); const [name, setName] = useState(''); @@ -67,18 +67,29 @@ export default function NewsletterSubscribe({ const handleSubmit = (event: React.FormEvent) => { setStatus(FormStatus.LOADING); event.preventDefault(); + + //validate email + const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; + if (!emailPattern.test(email)) { + setFormStatus(FormStatus.ERROR); + return; + } + setEmailError(''); // Clear error if valid + + setStatus(FormStatus.LOADING); + //end const data = { name, email, - interest: type + interest: type, }; fetch('/.netlify/functions/newsletter_subscription', { method: 'POST', body: JSON.stringify(data), headers: { - 'Content-Type': 'application/json' - } + 'Content-Type': 'application/json', + }, }) .then((res) => { if (res.status === 200) { @@ -95,11 +106,16 @@ export default function NewsletterSubscribe({ if (status === FormStatus.SUCCESS) { return ( -
- +
+ {ready ? t('successTitle') : 'Thank you for subscribing!'} - + {ready ? t('subtitle') : subtitle}
@@ -108,13 +124,21 @@ export default function NewsletterSubscribe({ if (status === FormStatus.ERROR) { return ( -
- +
+ {ready ? t('errorTitle') : 'Something went wrong'} - + {ready ? t('errorSubtitle') : errorSubtitle}{' '} - + {ready ? t('errorLinkText') : 'here'} @@ -123,27 +147,39 @@ export default function NewsletterSubscribe({ } return ( -
- +
+ {ready ? t('title') : title} - + {ready ? t('subtitle') : subtitle} {status === 'loading' ? ( - } dark={dark} /> + } + dark={dark} + /> ) : ( -
+ )} From 65c110c8a068d7689792d13040af9cba40e08527 Mon Sep 17 00:00:00 2001 From: Nalin Date: Sat, 1 Mar 2025 21:56:25 +0530 Subject: [PATCH 03/24] Removed redundancy, declared --- components/NewsletterSubscribe.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/components/NewsletterSubscribe.tsx b/components/NewsletterSubscribe.tsx index 52ef4559d0e0..df82660591e6 100644 --- a/components/NewsletterSubscribe.tsx +++ b/components/NewsletterSubscribe.tsx @@ -51,7 +51,7 @@ export default function NewsletterSubscribe({ const [email, setEmail] = useState(''); const [name, setName] = useState(''); const [status, setStatus] = useState(FormStatus.NORMAL); - + const [emailError, setEmailError] = useState(''); const { t, ready } = useTranslation('common', { keyPrefix: 'newsletterCTA' }); const headTextColor = dark ? 'text-white' : ''; @@ -75,8 +75,6 @@ export default function NewsletterSubscribe({ return; } setEmailError(''); // Clear error if valid - - setStatus(FormStatus.LOADING); //end const data = { name, From 18a0b998e21c87c66d4eda787988a78106e6d595 Mon Sep 17 00:00:00 2001 From: Nalin Date: Sat, 1 Mar 2025 22:15:13 +0530 Subject: [PATCH 04/24] Refactor --- components/NewsletterSubscribe.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/components/NewsletterSubscribe.tsx b/components/NewsletterSubscribe.tsx index df82660591e6..e13f0ee3f4a4 100644 --- a/components/NewsletterSubscribe.tsx +++ b/components/NewsletterSubscribe.tsx @@ -51,7 +51,8 @@ export default function NewsletterSubscribe({ const [email, setEmail] = useState(''); const [name, setName] = useState(''); const [status, setStatus] = useState(FormStatus.NORMAL); - const [emailError, setEmailError] = useState(''); + const [emailError, setEmailError] = useState<(error: string) => void>(); + const { t, ready } = useTranslation('common', { keyPrefix: 'newsletterCTA' }); const headTextColor = dark ? 'text-white' : ''; @@ -71,10 +72,12 @@ export default function NewsletterSubscribe({ //validate email const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; if (!emailPattern.test(email)) { + setEmailError('Invalid email address'); setFormStatus(FormStatus.ERROR); return; } setEmailError(''); // Clear error if valid + setStatus(FormStatus.LOADING); //end const data = { name, From 8f2db81b6993216a3d4eb3b648c242e24365afa5 Mon Sep 17 00:00:00 2001 From: Nalin Date: Sun, 2 Mar 2025 18:19:29 +0530 Subject: [PATCH 05/24] solved 5.2-> added necessary whitespaces to make it uniform solved 5.3 -> Font size on /blog endpoint matched with homepage --- pages/blog/index.tsx | 118 +++++++++++++++++++++++++------------------ 1 file changed, 68 insertions(+), 50 deletions(-) diff --git a/pages/blog/index.tsx b/pages/blog/index.tsx index 1e880f82d18f..09b186070b16 100644 --- a/pages/blog/index.tsx +++ b/pages/blog/index.tsx @@ -14,16 +14,13 @@ import type { IBlogPost } from '@/types/post'; import { HeadingLevel, HeadingTypeStyle } from '@/types/typography/Heading'; import { ParagraphTypeStyle } from '@/types/typography/Paragraph'; -/** - * @description The BlogIndexPage is the blog index page of the website. - */ export default function BlogIndexPage() { const router = useRouter(); const { navItems } = useContext(BlogContext); const [posts, setPosts] = useState([]); const [isClient, setIsClient] = useState(false); - const [visiblePosts, setVisiblePosts] = useState(9); // Number of posts initially visible + const [visiblePosts, setVisiblePosts] = useState(9); useEffect(() => { if (navItems) { @@ -45,21 +42,21 @@ export default function BlogIndexPage() { setIsClient(true); }, []); - const loadMore = () => { - setVisiblePosts((prev) => prev + 9); // Load additional 9 posts - }; + const loadMore = () => setVisiblePosts((prev) => prev + 9); const onFilter = (data: IBlogPost[]) => { setPosts(data); - setVisiblePosts(9); // Reset visible posts on filter change + setVisiblePosts(9); }; - const toFilter = [{ name: 'type' }, { name: 'authors', unique: 'name' }, { name: 'tags' }]; + const toFilter = [ + { name: 'type' }, + { name: 'authors', unique: 'name' }, + { name: 'tags' }, + ]; const clearFilters = () => { - router.push(`${router.pathname}`, undefined, { - shallow: true - }); + router.push(router.pathname, undefined, { shallow: true }); }; const showClearFilters = Object.keys(router.query).length > 0; @@ -68,79 +65,100 @@ export default function BlogIndexPage() { const image = '/img/social/blog.webp'; return ( - -
-
-
-
-
-
- + +
+
+
+ Welcome to our blog! - - Find the latest and greatest stories from our community + + Find the latest and greatest stories from our community. - + Want to publish a blog post? We love community stories.{' '} - + Submit yours! - - We have an + + We have an{' '} RSS feed - RSS Feed, too! + className="inline ml-1 text-primary-500 hover:text-primary-300" + src="/img/logos/rss.svg" + alt="RSS feed" + height="18px" + width="18px" + />{' '} + RSS Feed, too!
-
+ + {/* Filter Section */} +
{showClearFilters && ( )}
+ + {/* Blog Posts Section */}
- {Object.keys(posts).length === 0 && ( -
+ {posts.length === 0 && ( +
-

No post matches your filter

+

+ No post matches your filter +

)} - {Object.keys(posts).length > 0 && isClient && ( -