From 6d6ab9c3bf921b917c945daf2e69a2a0ffd7f61c Mon Sep 17 00:00:00 2001 From: Sunny Sahsi Date: Thu, 9 Nov 2023 23:09:58 +0530 Subject: [PATCH 1/9] feat: add functionality for redirect ot original url --- next.config.js | 13 +++- public/assets/icons/redirect.tsx | 7 +++ src/constants/url.ts | 2 + src/pages/redirect/[redirect].tsx | 101 ++++++++++++++++++++++++++++++ src/types/url.types.ts | 15 +++++ src/utils/fetchOriginalUrl .ts | 20 ++++++ 6 files changed, 157 insertions(+), 1 deletion(-) create mode 100644 public/assets/icons/redirect.tsx create mode 100644 src/pages/redirect/[redirect].tsx create mode 100644 src/types/url.types.ts create mode 100644 src/utils/fetchOriginalUrl .ts diff --git a/next.config.js b/next.config.js index 658404a..d98bf89 100644 --- a/next.config.js +++ b/next.config.js @@ -1,4 +1,15 @@ /** @type {import('next').NextConfig} */ -const nextConfig = {}; + +const nextConfig = { + async redirects() { + return [ + { + source: '/:shortCode', + destination: '/redirect/:shortCode', + permanent: false, + }, + ]; + }, +}; module.exports = nextConfig; diff --git a/public/assets/icons/redirect.tsx b/public/assets/icons/redirect.tsx new file mode 100644 index 0000000..5f1c95c --- /dev/null +++ b/public/assets/icons/redirect.tsx @@ -0,0 +1,7 @@ +const RedirectIcon = () => ( + + + +); + +export default RedirectIcon; diff --git a/src/constants/url.ts b/src/constants/url.ts index 60eb96d..e4e285a 100644 --- a/src/constants/url.ts +++ b/src/constants/url.ts @@ -2,3 +2,5 @@ export const TINY_API_URL = 'https://staging-tinysite-api.realdevsquad.com/v1'; export const TINY_API_GOOGLE_LOGIN = `${TINY_API_URL}/auth/google/login`; export const TINY_API_LOGOUT = `${TINY_API_URL}/auth/logout`; export const BASE_SHORT_URL = 'https://staging-tinysite.realdevsquad.com'; +export const TINY_API_URL_DETAIL = `${TINY_API_URL}/urls`; +export const TINY_API_REDIRECT = `${TINY_API_URL}/tinyurl`; diff --git a/src/pages/redirect/[redirect].tsx b/src/pages/redirect/[redirect].tsx new file mode 100644 index 0000000..32b2dd5 --- /dev/null +++ b/src/pages/redirect/[redirect].tsx @@ -0,0 +1,101 @@ +import fetchOriginalUrl from '@/utils/fetchOriginalUrl '; +import RedirectIcon from '../../../public/assets/icons/redirect'; +import { useState, useEffect } from 'react'; +import { useRouter } from 'next/router'; +import Head from 'next/head'; + +const Redirect = () => { + const router = useRouter(); + const { redirect: shortUrlCode } = router.query as { redirect: string }; + + const [originalUrl, setOriginalUrl] = useState(''); + const [timer, setTimer] = useState(5); + const [isPremiumUser, setIsPremiumUser] = useState(false); + const [showTooltip, setShowTooltip] = useState(false); + + useEffect(() => { + setIsPremiumUser(false); + }, []); + + useEffect(() => { + if (shortUrlCode) { + fetchOriginalUrl(shortUrlCode) + .then((url) => { + if (url) { + setOriginalUrl(url); + } else { + console.log('Short URL code not found'); + router.push('/'); + } + }) + .catch((error) => { + console.error('Error fetching original URL:', error); + }); + } + }, [shortUrlCode]); + + useEffect(() => { + if (timer > 0) { + const countdown = setTimeout(() => setTimer(timer - 1), 1000); + return () => clearTimeout(countdown); + } else if (timer === 0) { + router.push(originalUrl); + } + }, [timer, originalUrl]); + + const handleGoButtonClick = () => { + if (isPremiumUser) { + router.push(originalUrl); + } else { + setShowTooltip(true); + } + }; + + return ( + <> + + Redirecting... + + +
+

You are being redirected to:

+

{originalUrl}

+ {timer < 1 ? ( +
+ +

Redirecting...

+
+ ) : ( + <> +
+ {timer} +
+ + + )} + + {showTooltip && !isPremiumUser && ( +
+ Wait for {timer} seconds or become a premium user to skip the wait time. +
+ )} + +
+

This short URL generated by 

+ + Real Dev Squad URL Shortener + +
+
+ + ); +}; + +export default Redirect; diff --git a/src/types/url.types.ts b/src/types/url.types.ts new file mode 100644 index 0000000..0c1ddb6 --- /dev/null +++ b/src/types/url.types.ts @@ -0,0 +1,15 @@ +export interface UrlType { + Id: number; + OriginalUrl: string; + ShortUrl: string; + Comment: string; + UserId: number; + ExpiredAt: string; + CreatedAt: string; + CreatedBy: string; +} + +export interface UrlResponseTypes { + message: string; + url: UrlType; +} diff --git a/src/utils/fetchOriginalUrl .ts b/src/utils/fetchOriginalUrl .ts new file mode 100644 index 0000000..0c03429 --- /dev/null +++ b/src/utils/fetchOriginalUrl .ts @@ -0,0 +1,20 @@ +import { TINY_API_URL_DETAIL } from '@/constants/url'; +import { UrlResponseTypes } from '@/types/url.types'; + +async function fetchOriginalUrl(shortUrlCode: string): Promise { + try { + const response = await fetch(`${TINY_API_URL_DETAIL}/${shortUrlCode}`); + + if (response.ok) { + const data = (await response.json()) as UrlResponseTypes; + return data.url.OriginalUrl; + } else { + return null; // Return null if the response is not ok + } + } catch (error) { + console.error('Error fetching original URL:', error); + return null; // Return null in case of an error + } +} + +export default fetchOriginalUrl; From aea3d1fce236e58309d1dd51ff4e1d92a24b0ce7 Mon Sep 17 00:00:00 2001 From: Sunny Sahsi Date: Thu, 9 Nov 2023 23:31:15 +0530 Subject: [PATCH 2/9] refactor: redirect page --- src/pages/redirect/[redirect].tsx | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/pages/redirect/[redirect].tsx b/src/pages/redirect/[redirect].tsx index 32b2dd5..a2e08d6 100644 --- a/src/pages/redirect/[redirect].tsx +++ b/src/pages/redirect/[redirect].tsx @@ -15,9 +15,7 @@ const Redirect = () => { useEffect(() => { setIsPremiumUser(false); - }, []); - useEffect(() => { if (shortUrlCode) { fetchOriginalUrl(shortUrlCode) .then((url) => { @@ -32,16 +30,14 @@ const Redirect = () => { console.error('Error fetching original URL:', error); }); } - }, [shortUrlCode]); - useEffect(() => { if (timer > 0) { const countdown = setTimeout(() => setTimer(timer - 1), 1000); return () => clearTimeout(countdown); } else if (timer === 0) { router.push(originalUrl); } - }, [timer, originalUrl]); + }, [shortUrlCode, timer, originalUrl]); const handleGoButtonClick = () => { if (isPremiumUser) { From 44508dcc42c794258aafff6b51e19a7cfce72f02 Mon Sep 17 00:00:00 2001 From: Sunny Sahsi Date: Fri, 10 Nov 2023 01:49:13 +0530 Subject: [PATCH 3/9] refactor: redirect page and add visiticon for shorten url --- next.config.js | 13 +-------- public/assets/icons/share.tsx | 28 +++++++++++++++++++ .../[redirect].tsx => [slug]/index.tsx} | 19 ++++++------- src/pages/dashboard/index.tsx | 18 +++++++++--- 4 files changed, 52 insertions(+), 26 deletions(-) create mode 100644 public/assets/icons/share.tsx rename src/pages/{redirect/[redirect].tsx => [slug]/index.tsx} (82%) diff --git a/next.config.js b/next.config.js index d98bf89..658404a 100644 --- a/next.config.js +++ b/next.config.js @@ -1,15 +1,4 @@ /** @type {import('next').NextConfig} */ - -const nextConfig = { - async redirects() { - return [ - { - source: '/:shortCode', - destination: '/redirect/:shortCode', - permanent: false, - }, - ]; - }, -}; +const nextConfig = {}; module.exports = nextConfig; diff --git a/public/assets/icons/share.tsx b/public/assets/icons/share.tsx new file mode 100644 index 0000000..4de2c52 --- /dev/null +++ b/public/assets/icons/share.tsx @@ -0,0 +1,28 @@ +const ShareIcon = () => ( + + Visit + + + + +); + +export default ShareIcon; diff --git a/src/pages/redirect/[redirect].tsx b/src/pages/[slug]/index.tsx similarity index 82% rename from src/pages/redirect/[redirect].tsx rename to src/pages/[slug]/index.tsx index a2e08d6..c787ccb 100644 --- a/src/pages/redirect/[redirect].tsx +++ b/src/pages/[slug]/index.tsx @@ -3,13 +3,13 @@ import RedirectIcon from '../../../public/assets/icons/redirect'; import { useState, useEffect } from 'react'; import { useRouter } from 'next/router'; import Head from 'next/head'; +import { TINY_SITE } from '@/constants/url'; const Redirect = () => { const router = useRouter(); - const { redirect: shortUrlCode } = router.query as { redirect: string }; - + const { slug: shortUrlCode } = router.query as { slug: string }; const [originalUrl, setOriginalUrl] = useState(''); - const [timer, setTimer] = useState(5); + const [timer, setTimer] = useState(55555); const [isPremiumUser, setIsPremiumUser] = useState(false); const [showTooltip, setShowTooltip] = useState(false); @@ -53,9 +53,9 @@ const Redirect = () => { Redirecting... -
+

You are being redirected to:

-

{originalUrl}

+

{originalUrl}

{timer < 1 ? (
@@ -74,19 +74,18 @@ const Redirect = () => { {showTooltip && !isPremiumUser && (
- Wait for {timer} seconds or become a premium user to skip the wait time. + The skip feature is exclusively available to Premium users.
)}
diff --git a/src/pages/dashboard/index.tsx b/src/pages/dashboard/index.tsx index 0757960..ab70858 100644 --- a/src/pages/dashboard/index.tsx +++ b/src/pages/dashboard/index.tsx @@ -5,9 +5,10 @@ import { urlRegex } from '@/utils/constants'; import InputBox from '@/components/InputBox'; import Button from '@/components/Button'; import CopyIcon from '../../../public/assets/icons/copy'; +import ShareIcon from '../../../public/assets/icons/share'; import Toast from '@/components/Toast'; import shortenUrl from '@/utils/shortenUrl'; -import { BASE_SHORT_URL } from '@/constants/url'; +import { TINY_SITE } from '@/constants/url'; interface InputSectionProps { url: string; @@ -31,7 +32,7 @@ const InputSection: React.FC = ({ url, setUrl, handleUrl }) = placeholder="🔗 Enter the URL" name="URL" /> -
@@ -47,9 +48,18 @@ const OutputSection: React.FC = ({ shortUrl, handleCopyUrl } value={shortUrl} placeholder="Copy the URL" /> + + + + + ); + } +}; + +const RedirectFooter = () => ( + +); const Redirect = () => { const router = useRouter(); @@ -17,18 +58,19 @@ const Redirect = () => { setIsPremiumUser(false); if (shortUrlCode) { - fetchOriginalUrl(shortUrlCode) - .then((url) => { + const fetchOriginalUrlAsync = async () => { + try { + const url = await fetchOriginalUrl(shortUrlCode); if (url) { setOriginalUrl(url); } else { - console.log('Short URL code not found'); router.push('/'); } - }) - .catch((error) => { + } catch (error) { console.error('Error fetching original URL:', error); - }); + } + }; + fetchOriginalUrlAsync(); } if (timer > 0) { @@ -56,38 +98,14 @@ const Redirect = () => {

You are being redirected to:

{originalUrl}

- {timer < 1 ? ( -
- -

Redirecting...

-
- ) : ( - <> -
- {timer} -
- - - )} + {showTooltip && !isPremiumUser && (
The skip feature is exclusively available to Premium users.
)} - - +
); From 4020f9357dfc416e9ccdad3d09c57898c8ce6851 Mon Sep 17 00:00:00 2001 From: Sunny Sahsi Date: Sat, 11 Nov 2023 08:48:11 +0530 Subject: [PATCH 7/9] refactor : change anchor tag to Link tag --- src/components/Footer/index.tsx | 5 +++-- src/components/Navbar/index.tsx | 24 +++++++++++++----------- src/pages/[redirect]/index.tsx | 5 +++-- src/pages/dashboard/index.tsx | 18 ++++++++++-------- 4 files changed, 29 insertions(+), 23 deletions(-) diff --git a/src/components/Footer/index.tsx b/src/components/Footer/index.tsx index 354a723..5128682 100644 --- a/src/components/Footer/index.tsx +++ b/src/components/Footer/index.tsx @@ -1,3 +1,4 @@ +import Link from 'next/link'; import React from 'react'; const Footer: React.FC = () => { @@ -5,14 +6,14 @@ const Footer: React.FC = () => {

The contents of this website are deployed from this{' '} - open sourced repo - +

); diff --git a/src/components/Navbar/index.tsx b/src/components/Navbar/index.tsx index 804aceb..7bd7946 100644 --- a/src/components/Navbar/index.tsx +++ b/src/components/Navbar/index.tsx @@ -1,10 +1,12 @@ import React, { useEffect, useState } from 'react'; +import { TINY_API_GOOGLE_LOGIN, TINY_API_LOGOUT } from '@/constants/url'; + import Button from '@/components/Button'; -import ProfileIcon from '../ProfileIcon/ProfileIcon'; -import GoogleIcon from '../../../public/assets/icons/google'; import DownArrowIcon from '../../../public/assets/icons/downArrow'; +import GoogleIcon from '../../../public/assets/icons/google'; import IsAuthenticated from '@/hooks/isAuthenticated'; -import { TINY_API_GOOGLE_LOGIN, TINY_API_LOGOUT } from '@/constants/url'; +import Link from 'next/link'; +import ProfileIcon from '../ProfileIcon/ProfileIcon'; const Navbar: React.FC = () => { const [menuOpen, setMenuOpen] = useState(false); @@ -30,9 +32,9 @@ const Navbar: React.FC = () => { return (