diff --git a/admin/docker-compose.yaml b/admin/docker-compose.yaml index c897bfdd..6dfc305e 100644 --- a/admin/docker-compose.yaml +++ b/admin/docker-compose.yaml @@ -5,8 +5,6 @@ services: image: ${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com/blog-admin:${GITHUB_SHA}-${GITHUB_RUN_ATTEMPT}-blog-admin deploy: replicas: 1 - volumes: - - uploads:/srv/app/public/uploads ports: - "1337:1337" environment: @@ -26,6 +24,11 @@ services: AWS_BUCKET: ${AWS_BUCKET} AWS_BUCKET_URL: ${AWS_BUCKET_URL} HR_FROM_MAIL: ${HR_FROM_MAIL} + logging: + driver: awslogs + options: + awslogs-region: ${AWS_REGION} + awslogs-group: canopas-blog-admin-logs nginx: image: nginx:latest @@ -42,7 +45,3 @@ networks: outside: external: name: "host" - -volumes: - blog-data: - uploads: diff --git a/admin/src/api/post/content-types/post/lifecycles.js b/admin/src/api/post/content-types/post/lifecycles.js index 81dcb3a2..add31d57 100644 --- a/admin/src/api/post/content-types/post/lifecycles.js +++ b/admin/src/api/post/content-types/post/lifecycles.js @@ -128,10 +128,7 @@ async function TagsInput(tags) { where: { slug }, }); - const insert = existingTag && existingTag.name != tags[i].name; - slug = insert ? slug + "-" + i : slug; - - if (existingTag == null || insert) { + if (existingTag == null) { existingTag = await strapi.db.query("api::tag.tag").create({ data: { slug, diff --git a/website/.env.example b/website/.env.example deleted file mode 100644 index b24eb455..00000000 --- a/website/.env.example +++ /dev/null @@ -1,7 +0,0 @@ -NEXT_PUBLIC_IFRAMELY_KEY= -NEXT_PUBLIC_RECAPTCHA_SITE_KEY= -NEXT_PUBLIC_MIXPANEL_PROJECT_TOKEN= -NEXT_PUBLIC_STRAPI_URL= -NEXT_PUBLIC_STRAPI_DOMAIN= -NEXT_PUBLIC_WEBSITE_URL= -NEXT_PUBLIC_API_BASE= diff --git a/website/.eslintignore b/website/.eslintignore deleted file mode 100644 index 9fe4d3ea..00000000 --- a/website/.eslintignore +++ /dev/null @@ -1,6 +0,0 @@ -.next -out -node_modules -package-lock.json -package.json -.vscode \ No newline at end of file diff --git a/website/.eslintrc.json b/website/.eslintrc.json deleted file mode 100644 index bffb357a..00000000 --- a/website/.eslintrc.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "extends": "next/core-web-vitals" -} diff --git a/website/Dockerfile b/website/Dockerfile deleted file mode 100644 index fd07fdef..00000000 --- a/website/Dockerfile +++ /dev/null @@ -1,41 +0,0 @@ -FROM node:20-alpine AS base - -FROM base AS deps -RUN apk add --no-cache libc6-compat -WORKDIR /app - -COPY package.json yarn.lock* ./ -RUN yarn install --frozen-lockfile - -# Rebuild the source code only when needed -FROM base AS builder -WORKDIR /app -COPY --from=deps /app/node_modules ./node_modules -COPY . . - -RUN yarn build - -# Production image, copy all the files and run next -FROM base AS runner -WORKDIR /app - -ENV NODE_ENV production - -RUN addgroup --system --gid 1001 nodejs -RUN adduser --system --uid 1001 nextjs - -RUN mkdir .next -RUN chown nextjs:nodejs .next - -COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ -COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static - -USER nextjs - -EXPOSE 3000 - -ENV PORT 3000 -# set hostname to localhost -ENV HOSTNAME "0.0.0.0" - -CMD ["node", "server.js"] \ No newline at end of file diff --git a/website/assets/css/global.css b/website/assets/css/global.css deleted file mode 100644 index ea39e0f6..00000000 --- a/website/assets/css/global.css +++ /dev/null @@ -1,288 +0,0 @@ -@tailwind base; -@tailwind components; -@tailwind utilities; - -@font-face { - font-display: swap; - font-family: "Source CodePro"; - font-style: normal; - font-weight: 400; - src: url(./../fonts/Source-codePro.woff2) format("woff2"); -} - -@font-face { - font-display: swap; - font-family: "Poppins Regular"; - font-style: normal; - font-weight: 400; - src: url(./../fonts/Poppins-Regular.woff2) format("woff2"); -} - -@font-face { - font-display: swap; - font-family: "Poppins Medium"; - font-style: medium; - font-weight: 500; - src: url(./../fonts/Poppins-Medium.woff2) format("woff2"); -} - -input:-webkit-autofill, -input:-webkit-autofill:hover, -input:-webkit-autofill:focus, -input:-webkit-autofill:active { - transition: background-color 5000s !important; -} - -.floating-input:-webkit-autofill, -.floating-input:-webkit-autofill:hover, -.floating-input:-webkit-autofill:focus, -.floating-input:-webkit-autofill:active { - -webkit-text-fill-color: #ffffff !important; -} - -.cta-section, -.header-section, -.footer-section { - -webkit-user-select: none; - -moz-user-select: none; -} - -.cta-box-shadow { - -webkit-appearance: none; - -webkit-box-shadow: 0 10px 15px rgba(0, 0, 0, 0.25); - box-shadow: 0 10px 15px rgba(0, 0, 0, 0.25); -} - -body { - @apply font-inter font-normal break-words text-black-core/[0.87]; -} - -.content > p { - @apply pb-8; -} - -.prose > * { - @apply text-lg md:text-[1.1875rem] md:leading-8; -} - -.comment input[type="text"], -.comment input[type="email"], -.comment textarea { - @apply my-2 border border-solid border-gray-300 rounded bg-white bg-clip-padding px-3 py-1.5 font-normal text-base text-black-core/[0.75] transition ease-in-out focus:border-gray-300 focus:outline-none focus:bg-white focus:text-black-core/[0.75]; -} - -.comment input[type="submit"] { - @apply mt-3 rounded bg-blue-500 py-2 px-4 text-white font-bold hover:bg-blue-700; -} - -.gradient-text { - @apply bg-clip-text bg-gradient-to-r from-[#f2709c] via-[#ff909c] to-[#ff9472] text-transparent; -} - -.hoverable-text:hover { - @apply bg-clip-text bg-gradient-to-r from-[#f2709c] via-[#ff909c] to-[#ff9472] text-transparent; -} - -.gradient-border { - border-image: linear-gradient(to bottom, #f2709c, #ff835b) 1; -} - -.gradient-border-btn { - @apply border border-solid border-transparent rounded-[0.6rem] bg-gradient-to-r from-[#f2709c] via-[#ff909c] to-[#ff9472] shadow-[inset_2px_1000px_1px_#fff] p-[1rem] text-center hover:border hover:border-solid hover:border-transparent hover:from-[#f2709c] hover:to-[#ff9472] hover:shadow-none active:scale-[0.98]; -} - -.gradient-border-btn > a > .fab { - @apply text-[#f2709c]; -} - -.gradient-border-btn > span { - @apply my-0 mx-1.5 text-[#3d3d3d]; -} - -.gradient-border-btn:hover > span, -.gradient-border-btn:hover > span > span { - @apply text-[#fff]; -} - -.gradient-border-btn:hover > .fa, -.gradient-border-btn:hover > a > .fab, -.gradient-border-btn:hover > .arrow { - @apply text-white; -} - -.footer-icon > path, -button:hover > div > .footer-icon > path { - fill: url("#lgrad"); -} - -.gradient-border-btn:hover > a > .footer-icon > path, -button > div > .footer-icon > path { - fill: #ffffff; -} - -ul { - @apply pl-4; -} - -b, -strong { - @apply font-comme tracking-[0.01em] !text-black-core/[1] !font-medium text-[1.0625rem] md:text-[1.15625rem]; -} - -/* HighlightJS */ -/* Tomorrow Night Theme */ -/* https://jmblog.github.io/color-themes-for-google-code-highlightjs */ -/* Original theme - https://github.com/chriskempson/tomorrow-theme */ -/* https://jmblog.github.io/color-themes-for-google-code-highlightjs */ -.tomorrow-comment, -pre .hljs-comment, -pre .hljs-title { - @apply text-[#969896]; -} - -.tomorrow-red, -pre .hljs-variable, -pre .hljs-attribute, -pre .hljs-tag, -pre .hljs-regexp, -pre .ruby .hljs-constant, -pre .xml .hljs-tag .hljs-title, -pre .xml .hljs-pi, -pre .xml .hljs-doctype, -pre .html .hljs-doctype, -pre .css .id, -pre .css .hljs-class, -pre .css .hljs-pseudo { - @apply text-[#e49176]; -} - -.tomorrow-orange, -pre .hljs-number, -pre .hljs-preprocessor, -pre .hljs-built_in, -pre .hljs-literal, -pre .hljs-params, -pre .hljs-constant { - @apply text-[#de935f]; -} - -.tomorrow-yellow, -pre .hljs-class, -pre .ruby .hljs-class .hljs-title, -pre .css .hljs-rules .hljs-attribute { - @apply text-[#f0c674]; -} - -.tomorrow-green, -pre .hljs-string, -pre .hljs-value, -pre .hljs-inheritance, -pre .hljs-header, -pre .ruby .hljs-symbol, -pre .xml .hljs-cdata { - @apply text-[#b5bd68]; -} - -.tomorrow-aqua, -pre .css .hljs-hexcolor { - @apply text-[#8abeb7]; -} - -.tomorrow-blue, -pre .hljs-function, -pre .python .hljs-decorator, -pre .python .hljs-title, -pre .ruby .hljs-function .hljs-title, -pre .ruby .hljs-title .hljs-keyword, -pre .perl .hljs-sub, -pre .javascript .hljs-title, -pre .coffeescript .hljs-title { - @apply text-[#81a2be]; -} - -.tomorrow-purple, -pre .hljs-keyword, -pre .javascript .hljs-function { - @apply text-[#b294bb]; -} - -code::before, -code::after { - @apply content-none !important; -} - -.prose h1, -.prose h1 strong { - @apply font-comme text-[#000] text-[1.6875rem] md:text-[1.78125rem] lg:text-[1.95rem] xl:text-[2.0625rem] !-mb-2.5 !tracking-tight font-semibold !leading-snug mt-[3.75rem]; -} - -.prose h2, -.prose h2 strong { - @apply font-comme text-[#000] xl:text-[2.0625rem] !mb-4 !tracking-tight font-semibold; -} - -.prose hr { - @apply !my-6; -} - -.prose i, -.prose a { - @apply font-comme text-lg text-black-core/[0.87] tracking-[0.03rem] font-normal; -} - -.prose span a { - @apply font-comme text-lg text-blue-700 tracking-[0.03rem] font-normal; -} - -.prose - :where(code, code strong):not(:where([class~="not-prose"], blockquote code)) { - @apply bg-gray-200 py-0.5 px-1 rounded font-[550] tracking-wide font-source-codepro; -} - -.prose :where(pre code):not(:where([class~="not-prose"] *)) { - @apply bg-transparent text-[#dbe0e0] leading-normal tracking-[-0.022em] !break-normal; -} - -.prose :where(blockquote, blockquote i):not(:where([class~="not-prose"] *)) { - @apply not-italic !text-[1.4rem] lg:!text-[1.755rem] !leading-[2.3rem] !text-black-core/[0.75] !font-extralight tracking-wide font-inter; -} - -.prose :where(blockquote strong):not(:where([class~="not-prose"] *)) { - @apply not-italic !text-[1.4rem] lg:!text-[1.755rem] !leading-[2.3rem] !text-black-core/[0.75] !font-normal tracking-wide font-inter; -} - -.prose - :where(blockquote p:first-of-type):not( - :where([class~="not-prose"] *) - )::before { - @apply content-none; -} - -.category-swiper .swiper-slide { - @apply !w-auto text-[1.04rem] hoverable-text hover:cursor-pointer; -} - -.category-swiper .swiper-button-next:after, -.category-swiper .swiper-button-prev:after { - @apply px-7 pb-4 pt-1 text-black-900 !text-xs; -} - -.category-swiper .swiper-button-prev:after { - @apply bg-gradient-to-r from-white via-white to-[#ffffff82]; -} - -.category-swiper .swiper-button-next:after { - @apply bg-gradient-to-r from-[#ffffff82] via-white to-white; -} - -.category-swiper .swiper-button-disabled:after { - @apply opacity-0; -} - -.category-swiper .swiper-wrapper { - @apply !flex !flex-row space-x-14 md:space-x-20 !transition-all !duration-1000; -} - -.grecaptcha-badge { - @apply !hidden; -} diff --git a/website/assets/fonts/Comme-Light.woff2 b/website/assets/fonts/Comme-Light.woff2 deleted file mode 100644 index 06a44e05..00000000 Binary files a/website/assets/fonts/Comme-Light.woff2 and /dev/null differ diff --git a/website/assets/fonts/Comme-Medium.woff2 b/website/assets/fonts/Comme-Medium.woff2 deleted file mode 100644 index 781e0325..00000000 Binary files a/website/assets/fonts/Comme-Medium.woff2 and /dev/null differ diff --git a/website/assets/fonts/Comme-Regular.woff2 b/website/assets/fonts/Comme-Regular.woff2 deleted file mode 100644 index 938292c6..00000000 Binary files a/website/assets/fonts/Comme-Regular.woff2 and /dev/null differ diff --git a/website/assets/fonts/Comme-SemiBold.woff2 b/website/assets/fonts/Comme-SemiBold.woff2 deleted file mode 100644 index 421c19fd..00000000 Binary files a/website/assets/fonts/Comme-SemiBold.woff2 and /dev/null differ diff --git a/website/assets/fonts/Inter-Bold.woff2 b/website/assets/fonts/Inter-Bold.woff2 deleted file mode 100644 index 2846f29c..00000000 Binary files a/website/assets/fonts/Inter-Bold.woff2 and /dev/null differ diff --git a/website/assets/fonts/Inter-ExtraLight.woff2 b/website/assets/fonts/Inter-ExtraLight.woff2 deleted file mode 100644 index 621c7bea..00000000 Binary files a/website/assets/fonts/Inter-ExtraLight.woff2 and /dev/null differ diff --git a/website/assets/fonts/Inter-Medium.woff2 b/website/assets/fonts/Inter-Medium.woff2 deleted file mode 100644 index f92498a2..00000000 Binary files a/website/assets/fonts/Inter-Medium.woff2 and /dev/null differ diff --git a/website/assets/fonts/Inter-Regular.woff2 b/website/assets/fonts/Inter-Regular.woff2 deleted file mode 100644 index 6c2b6893..00000000 Binary files a/website/assets/fonts/Inter-Regular.woff2 and /dev/null differ diff --git a/website/assets/fonts/Inter-SemiBold.woff2 b/website/assets/fonts/Inter-SemiBold.woff2 deleted file mode 100644 index 611e90c9..00000000 Binary files a/website/assets/fonts/Inter-SemiBold.woff2 and /dev/null differ diff --git a/website/assets/fonts/Poppins-Medium.woff2 b/website/assets/fonts/Poppins-Medium.woff2 deleted file mode 100644 index ceecc77a..00000000 Binary files a/website/assets/fonts/Poppins-Medium.woff2 and /dev/null differ diff --git a/website/assets/fonts/Poppins-Regular.woff2 b/website/assets/fonts/Poppins-Regular.woff2 deleted file mode 100644 index 4aae28cf..00000000 Binary files a/website/assets/fonts/Poppins-Regular.woff2 and /dev/null differ diff --git a/website/assets/fonts/Source-codePro.woff2 b/website/assets/fonts/Source-codePro.woff2 deleted file mode 100644 index 77af0877..00000000 Binary files a/website/assets/fonts/Source-codePro.woff2 and /dev/null differ diff --git a/website/assets/images/404page_4_1.svg b/website/assets/images/404page_4_1.svg deleted file mode 100644 index 41db5822..00000000 --- a/website/assets/images/404page_4_1.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/website/assets/images/404page_4_2.svg b/website/assets/images/404page_4_2.svg deleted file mode 100644 index 581dc996..00000000 --- a/website/assets/images/404page_4_2.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/website/assets/images/cta/fifth-cta-800w.webp b/website/assets/images/cta/fifth-cta-800w.webp deleted file mode 100644 index c4ea05bf..00000000 Binary files a/website/assets/images/cta/fifth-cta-800w.webp and /dev/null differ diff --git a/website/assets/images/cta/first-cta-2400w.webp b/website/assets/images/cta/first-cta-2400w.webp deleted file mode 100644 index bb35f5a0..00000000 Binary files a/website/assets/images/cta/first-cta-2400w.webp and /dev/null differ diff --git a/website/assets/images/cta/first-cta-400w.webp b/website/assets/images/cta/first-cta-400w.webp deleted file mode 100644 index 79632754..00000000 Binary files a/website/assets/images/cta/first-cta-400w.webp and /dev/null differ diff --git a/website/assets/images/cta/fourth-cta-2400w.webp b/website/assets/images/cta/fourth-cta-2400w.webp deleted file mode 100644 index 0e58246e..00000000 Binary files a/website/assets/images/cta/fourth-cta-2400w.webp and /dev/null differ diff --git a/website/assets/images/cta/second-cta-2400.svg b/website/assets/images/cta/second-cta-2400.svg deleted file mode 100644 index 5e4e46d4..00000000 --- a/website/assets/images/cta/second-cta-2400.svg +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/website/assets/images/cta/second-cta-400.svg b/website/assets/images/cta/second-cta-400.svg deleted file mode 100644 index a946fbde..00000000 --- a/website/assets/images/cta/second-cta-400.svg +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/website/assets/images/emailTemplate.webp b/website/assets/images/emailTemplate.webp deleted file mode 100644 index a3aab1e1..00000000 Binary files a/website/assets/images/emailTemplate.webp and /dev/null differ diff --git a/website/assets/images/footer/new-bg.svg b/website/assets/images/footer/new-bg.svg deleted file mode 100644 index 2ed69148..00000000 --- a/website/assets/images/footer/new-bg.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/website/assets/images/icon.svg b/website/assets/images/icon.svg deleted file mode 100644 index 67a2582d..00000000 --- a/website/assets/images/icon.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/website/assets/images/loader.svg b/website/assets/images/loader.svg deleted file mode 100644 index 15a2594a..00000000 --- a/website/assets/images/loader.svg +++ /dev/null @@ -1,52 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/website/assets/images/logo-header.svg b/website/assets/images/logo-header.svg deleted file mode 100644 index b704e1cb..00000000 --- a/website/assets/images/logo-header.svg +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/website/assets/images/small-loader.svg b/website/assets/images/small-loader.svg deleted file mode 100644 index 6e36eba8..00000000 --- a/website/assets/images/small-loader.svg +++ /dev/null @@ -1,52 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/website/assets/images/user.png b/website/assets/images/user.png deleted file mode 100644 index b7a7532f..00000000 Binary files a/website/assets/images/user.png and /dev/null differ diff --git a/website/components/authorDetails.js b/website/components/authorDetails.js deleted file mode 100644 index da2f5473..00000000 --- a/website/components/authorDetails.js +++ /dev/null @@ -1,58 +0,0 @@ -import Image from "next/image"; - -export default function AuthorDetails({ postData }) { - return ( -
-
-
-
-
- {postData.authorAltText} -
-
-
- {postData.authorName} -
-
- {postData.authorBio} -
-
-
-
-
-
-
-
-
- {postData.authorAltText} -
-
-
- {postData.authorName} -
-
- {postData.authorBio} -
-
-
-
-
-
- ); -} diff --git a/website/components/comments/commentForm.js b/website/components/comments/commentForm.js deleted file mode 100644 index e478555a..00000000 --- a/website/components/comments/commentForm.js +++ /dev/null @@ -1,111 +0,0 @@ -import { useState, useRef, useEffect } from "react"; -import { faMessage } from "@fortawesome/free-solid-svg-icons"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import axios from "axios"; -import config from "../../config"; -const STRAPI_URL = config.STRAPI_URL; - -export default function CommentForm({ post, onNewComment, recaptchaRef }) { - const [showForm, setShowForm] = useState(false); - const formRef = useRef(null); - const [name, setName] = useState(""); - const [email, setEmail] = useState(""); - const [message, setMessage] = useState(""); - const [error, setError] = useState(""); - const [postId, parentId] = post; - - async function handleSubmit(event) { - event.preventDefault(); - - recaptchaRef.current.executeAsync().then(() => { - const commentData = { - data: { - postId, - name, - email, - comment: message, - parent_id: parentId ? parentId : null, - }, - }; - - axios - .post(`${STRAPI_URL}/v1/comments?populate=deep`, commentData) - .then((response) => { - onNewComment(response.data.data.attributes.post.data); - }) - .catch((error) => { - setError(error.response.data.error.message); - }); - - setName(""); - setEmail(""); - setMessage(""); - setError(""); - recaptchaRef.current.reset(); - }); - } - - useEffect(() => { - function handleClickOutside(event) { - if (formRef.current && !formRef.current.contains(event.target)) { - setShowForm(false); - } - } - document.addEventListener("mousedown", handleClickOutside); - }, [formRef]); - - return ( -
-
- - - Leave comment - -
-
- - - {showProjectInfoValidationError ? ( - - This field is required - - ) : ( - "" - )} -
- - -
- {showLoader ? ( - loader-image - ) : ( -
- {showErrorMessage ? ( -
- - {errorMessage} - -
- ) : ( - "" - )} - -
- )} -
- - - - - - ); -} diff --git a/website/components/cta/CTA3.js b/website/components/cta/CTA3.js deleted file mode 100644 index a32cf627..00000000 --- a/website/components/cta/CTA3.js +++ /dev/null @@ -1,296 +0,0 @@ -import React, { useState, useEffect } from "react"; -import Image from "next/image"; -import loaderImage from "../../assets/images/small-loader.svg"; -import { isValidEmail, isValidPhoneNumber } from "../../utils"; -import { submitFormData } from "./api"; - -export default function CTA() { - const [name, setName] = useState(""); - const [email, setEmail] = useState(""); - const [projectInfo, setProjectInfo] = useState(""); - const [phoneNumber, setPhoneNumber] = useState(""); - const [showNameValidationError, setShowNameValidationError] = useState(false); - const [showEmailValidationError, setShowEmailValidationError] = - useState(false); - const [showValidEmailError, setShowValidEmailError] = useState(false); - const [showProjectInfoValidationError, setShowProjectInfoValidationError] = - useState(false); - const [showPhoneNumberValidationError, setShowPhoneNumberValidationError] = - useState(false); - const [showValidPhoneNumberError, setShowValidPhoneNumberError] = - useState(false); - const [errorMessage, setErrorMessage] = useState( - "Something went wrong on our side", - ); - const [showLoader, setShowLoader] = useState(false); - const [showErrorMessage, setShowErrorMessage] = useState(false); - - const validateForm = () => { - setShowNameValidationError(name.trim().length === 0); - setShowEmailValidationError(email.trim().length === 0); - setShowProjectInfoValidationError(projectInfo.trim().length === 0); - setShowPhoneNumberValidationError(phoneNumber.trim().length === 0); - - return ( - showNameValidationError || - showEmailValidationError || - showValidEmailError || - showProjectInfoValidationError || - showPhoneNumberValidationError || - showValidPhoneNumberError - ); - }; - - useEffect(() => { - const myText = document.querySelector(".myTextarea"); - myText.style.minHeight = "100px"; - }, []); - - const submitForm = (event) => { - event.preventDefault(); - if (!validateForm()) { - setShowLoader(true); - } - - grecaptcha.enterprise.ready(() => { - grecaptcha.enterprise - .execute(process.env.NEXT_PUBLIC_RECAPTCHA_SITE_KEY, { - action: "verify", - }) - .then((token) => { - if (!validateForm()) { - let formData = { - name: name, - email: email, - project_info: projectInfo - ? projectInfo.replace(/\./g, ".\n") - : "NA", - phone_number: phoneNumber, - token, - }; - - submitFormData( - formData, - resetForm, - setShowLoader, - setShowErrorMessage, - setErrorMessage, - ); - } - }) - .catch(() => { - setErrorMessage("Invalid recaptcha score"); - - setShowErrorMessage(true); - setTimeout(() => { - setShowErrorMessage(false); - }, 3000); - }); - }); - }; - - const resetForm = () => { - setName(""); - setEmail(""); - setProjectInfo(""); - setPhoneNumber(""); - }; - - return ( - <> -
-
-
-
-
-
- Talk to an expert -
-
- get in - touch -
-
- Our team is happy to answer your questions. Fill out the form - and we’ll get back to you as soon as possible -
-
-
-
-
-
-
-
- setName(e.target.value)} - placeholder=" " - /> - - {showNameValidationError ? ( - - Name is required - - ) : ( - "" - )} -
- -
- setEmail(e.target.value)} - onBlur={() => { - setShowValidEmailError(isValidEmail(email)); - }} - placeholder=" " - /> - - {showEmailValidationError ? ( - - Email is required - - ) : ( - "" - )} - {email.trim().length != 0 && showValidEmailError ? ( - - Please enter valid email address - - ) : ( - "" - )} -
-
- setPhoneNumber(e.target.value)} - onBlur={() => { - setShowValidPhoneNumberError( - isValidPhoneNumber(phoneNumber), - ); - }} - placeholder=" " - /> - - {showPhoneNumberValidationError ? ( - - Phone number is required - - ) : ( - "" - )} - {phoneNumber.trim().length != 0 && - showValidPhoneNumberError ? ( - - Please enter valid Phone number - - ) : ( - "" - )} -
-
- - - {showProjectInfoValidationError ? ( - - This field is required - - ) : ( - "" - )} -
-
- -
- {showLoader ? ( - loader-image - ) : ( -
- {showErrorMessage ? ( -
- - {errorMessage} - -
- ) : ( - "" - )} - -
- )} -
-
-
-
-
-
-
- - ); -} diff --git a/website/components/cta/CTA4.js b/website/components/cta/CTA4.js deleted file mode 100644 index 16f0c80f..00000000 --- a/website/components/cta/CTA4.js +++ /dev/null @@ -1,59 +0,0 @@ -import React from "react"; -import Image from "next/image"; -import cta2400 from "../../assets/images/cta/fourth-cta-2400w.webp"; -import Link from "next/link"; -import config from "../../config"; - -export default function CTA() { - const reasons = [ - "High-performing mobile apps", - "Bulletproof cloud solutions", - "Custom solutions for your business.", - ]; - - return ( -
-
-
-
- fourth-cta-image -
- -
-
-

- Whether you need... -

-
    - {reasons.map((reason, index) => { - return ( -
  • - * -
    {reason}
    -
  • - ); - })} -
-
- Bring us your toughest challenge and we'll show you the - path to a sleek solution. -
- -
- - Talk to our experts - -
- -
-
-
-
-
- ); -} diff --git a/website/components/cta/CTA5.js b/website/components/cta/CTA5.js deleted file mode 100644 index 570140d0..00000000 --- a/website/components/cta/CTA5.js +++ /dev/null @@ -1,56 +0,0 @@ -import React from "react"; -import Image from "next/image"; -import cta800 from "../../assets/images/cta/fifth-cta-800w.webp"; -import Link from "next/link"; -import config from "../../config"; - -export default function CTA() { - return ( -
-
-
-
- -
-
-

- - L - - et's - - W - - ork -
- - T - - ogether -

-

- Not sure where to start? We also offer code and architecture - reviews, strategic planning, and more. -

- -
- - Get Free Consultation - -
- -
-
- cta-image - -
-
-
-
-
- ); -} diff --git a/website/components/cta/api.js b/website/components/cta/api.js deleted file mode 100644 index e70764ce..00000000 --- a/website/components/cta/api.js +++ /dev/null @@ -1,31 +0,0 @@ -import axios from "axios"; -import config from "../../config"; - -export function submitFormData( - formData, - resetForm, - setShowLoader, - setShowErrorMessage, - setErrorMessage, -) { - axios - .post(config.API_BASE + "/api/send-contact-mail", formData) - .then(() => { - localStorage.setItem("client-name", JSON.stringify(formData.name)); - window.location.href = config.WEBSITE_URL + "/thank-you"; - resetForm(); - }) - .catch((err) => { - if (err.response.status === 401) { - setErrorMessage("Invalid recaptcha score"); - - setShowErrorMessage(true); - setTimeout(() => { - setShowErrorMessage(false); - }, 3000); - } - }) - .finally(() => { - setShowLoader(false); - }); -} diff --git a/website/components/errors/serverError.js b/website/components/errors/serverError.js deleted file mode 100644 index cfdaf046..00000000 --- a/website/components/errors/serverError.js +++ /dev/null @@ -1,74 +0,0 @@ -import { Dialog, Transition } from "@headlessui/react"; -import { Fragment, useState } from "react"; - -export default function ServerError() { - let [isOpen, setIsOpen] = useState(true); - - function closeModal() { - setIsOpen(false); - } - - return ( - - -
- - - - - - -
- - Error - -
-

- Something went wrong !! -

-
- -
- -
-
-
-
-
-
- ); -} diff --git a/website/components/loader.js b/website/components/loader.js deleted file mode 100644 index ab3ca5c2..00000000 --- a/website/components/loader.js +++ /dev/null @@ -1,10 +0,0 @@ -import Image from "next/image"; -import loader from "./../assets/images/loader.svg"; - -export default function Loader() { - return ( -
- loader -
- ); -} diff --git a/website/components/partials/footer.js b/website/components/partials/footer.js deleted file mode 100644 index 52c7cde4..00000000 --- a/website/components/partials/footer.js +++ /dev/null @@ -1,266 +0,0 @@ -import React, { useState } from "react"; -import Image from "next/image"; -import bg from "../../assets/images/footer/new-bg.svg"; -import Link from "next/link"; -import axios from "axios"; -import config from "../../config"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { - faFacebookF, - faInstagram, - faTwitter, - faMediumM, - faLinkedinIn, - faYoutube, -} from "@fortawesome/free-brands-svg-icons"; -import { faBell, faXmark } from "@fortawesome/free-solid-svg-icons"; -import { faCopyright } from "@fortawesome/free-regular-svg-icons"; -import { isValidEmail } from "../../utils"; - -export default function Footer({ mixpanel }) { - const [email, setEmail] = useState(""); - const [alerts, setAlerts] = useState(false); - const [showValidEmailError, setShowValidEmailError] = useState(false); - - if (alerts) { - setTimeout(() => { - setAlerts(false); - }, 2000); - } - - const handleIconClick = (icon) => { - mixpanel.track(icon); - }; - - const handleSubscription = async (event) => { - event.preventDefault(); - - if (!showValidEmailError) { - await axios - .post(`${config.STRAPI_URL}/v1/user/subscribeUser?populate=deep`, { - email, - }) - .then(() => { - setAlerts(true); - }) - .catch((err) => { - console.log("Error:", err); - }); - - setEmail(""); - } - }; - return ( - <> -
- canopas-footer -
-
-
-
- Subscribe Here! -
-
-
- { - setShowValidEmailError(isValidEmail(email)); - }} - onChange={(e) => setEmail(e.target.value)} - required - /> -
- - - -
- {email.trim().length != 0 && showValidEmailError ? ( - - Please enter valid email address - - ) : ( - "" - )} -
-
-
- Follow us on -
-
    -
  • - { - handleIconClick("tap_footer_facebook"); - }} - aria-label="footerLink" - className="w-5 h-5 md:w-8 md:h-8" - > - - -
  • - -
  • - { - handleIconClick("tap_footer_instagram"); - }} - aria-label="footerLink" - className="w-5 h-5 md:w-8 md:h-8" - > - - -
  • - -
  • - { - handleIconClick("tap_footer_twitter"); - }} - aria-label="footerLink" - className="w-5 h-5 md:w-8 md:h-8" - > - - -
  • - -
  • - { - handleIconClick("tap_footer_medium"); - }} - aria-label="footerLink" - className="w-5 h-5 md:w-8 md:h-8" - > - - -
  • - -
  • - { - handleIconClick("tap_footer_linkedin"); - }} - aria-label="footerLink" - className="w-5 h-5 md:w-8 md:h-8" - > - - -
  • - -
  • - { - handleIconClick("tap_footer_youtube"); - }} - aria-label="footerLink" - className="w-5 h-5 md:w-8 md:h-8" - > - - -
  • -
-
-
- -
-
- -
- {new Date().getFullYear()} Canopas Software LLP. All rights - reserved. -
- - - - - - -
- {alerts ? ( -
-

Subscribe Successfully!

- { - setAlerts(false); - }} - /> -
- ) : ( - "" - )} -
- - ); -} diff --git a/website/components/partials/header.js b/website/components/partials/header.js deleted file mode 100644 index 2f8e0aed..00000000 --- a/website/components/partials/header.js +++ /dev/null @@ -1,383 +0,0 @@ -import Image from "next/image"; -import Logo from "../../assets/images/logo-header.svg"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { faBars, faXmark } from "@fortawesome/free-solid-svg-icons"; -import Link from "next/link"; -import config from "../../config"; -import { useRouter } from "next/router"; -import React, { useState, useEffect } from "react"; - -export default function Header({ mixpanel }) { - const HOST_URL = config.WEBSITE_URL; - const router = useRouter(); - const [showHeader, setShowHeader] = useState(true); - const [lastScrollPos, setLastScrollPos] = useState(0); - const [isMenuOpen, setIsMenuOpen] = useState(false); - const [showContributionMenu, setShowContributionMenu] = useState(false); - const [submenuTimeout, setSubmenuTimeout] = useState(null); - - const handleMenuClick = (Menu) => { - mixpanel.track(Menu); - }; - - const handleMouseEnter = () => { - clearTimeout(submenuTimeout); - setShowContributionMenu(true); - }; - - const handleMouseLeave = () => { - const timeout = setTimeout(() => { - setShowContributionMenu(false); - }, 100); - - setSubmenuTimeout(timeout); - }; - useEffect(() => { - const handleScroll = () => { - const currentScrollPos = window.pageYOffset; - setShowHeader(currentScrollPos < lastScrollPos); - - setLastScrollPos(currentScrollPos); - }; - - window.addEventListener("scroll", handleScroll); - return () => { - window.removeEventListener("scroll", handleScroll); - clearTimeout(submenuTimeout); - }; - }, [showHeader, lastScrollPos, submenuTimeout]); - - return ( - <> -
0 ? "drop-shadow-lg" : ""}` - : "!-top-44" - }` - } header-section`} - > - -
- - ); -} diff --git a/website/components/posts/postsList.js b/website/components/posts/postsList.js deleted file mode 100644 index 4305ed56..00000000 --- a/website/components/posts/postsList.js +++ /dev/null @@ -1,123 +0,0 @@ -import { useState, useEffect } from "react"; -import Link from "next/link"; -import Image from "next/image"; - -export default function PostsList({ postData, mixpanel }) { - const [posts, slug, tagName] = postData; - const [displayedPosts, setDisplayedPosts] = useState(10); - - useEffect(() => { - const handleScroll = () => { - if ( - window.innerHeight + document.documentElement.scrollTop >= - document.documentElement.offsetHeight - 100 - ) { - setDisplayedPosts((prev) => prev + 5); - } - }; - window.addEventListener("scroll", handleScroll); - return () => { - window.removeEventListener("scroll", handleScroll); - }; - }, []); - - return ( -
- {posts.slice(0, displayedPosts).map((post, i) => { - post = post.attributes; - - return ( -
- -
-
- {post.authorAltText} -
- - {post.authorName} - - - - {post.published_on} - -
-
-
-
{ - mixpanel.track("tap_blog_title", { - Title: post.title, - }); - }} - > - {post.title} -
-
- {post.summary} -
-
-
- {post.alternativeText -
-
- - - {slug != null ? ( - <> -
- - {tagName} - - - {post.readingTime} min read - -
- - ) : ( -
- {post.tags.map((tag) => { - return ( -
- - {tag.name} - -
- ); - })} -
- )} -
- ); - })} -
- ); -} diff --git a/website/components/posts/recommendedPosts.js b/website/components/posts/recommendedPosts.js deleted file mode 100644 index 1d03d291..00000000 --- a/website/components/posts/recommendedPosts.js +++ /dev/null @@ -1,92 +0,0 @@ -import Link from "next/link"; -import Image from "next/image"; - -export default function recommendedPosts({ postData }) { - let [relatedPosts, mixpanel] = postData; - - relatedPosts = relatedPosts.sort( - (a, b) => b.attributes.index - a.attributes.index, - ); - - return ( - <> - - Recommended for you - -
- {relatedPosts.map((post, i) => { - post = post.attributes; - post.published_on = new Date(post.published_on).toLocaleDateString( - "en-US", - { - month: "short", - day: "numeric", - }, - ); - if (i < 3) { - return ( - -
-
-
-
- {post.authorAltText} -
- - {post.authorName} - -
- -
{ - mixpanel.track("tap_blog_title", { - Title: post.title, - }); - }} - > - {post.title} -
-
- {post.summary} -
-
- {post.readingTime} min read | - Published on {post.published_on} -
-
-
- {post.alternativeText -
-
- - ); - } - })} -
- - ); -} diff --git a/website/config-prod.js b/website/config-prod.js deleted file mode 100644 index ffe5080b..00000000 --- a/website/config-prod.js +++ /dev/null @@ -1,36 +0,0 @@ -export default Object.freeze({ - STRAPI_URL: process.env.NEXT_PUBLIC_STRAPI_URL, - WEBSITE_URL: process.env.NEXT_PUBLIC_WEBSITE_URL, - API_BASE: process.env.NEXT_PUBLIC_API_BASE, - - WORDS_PER_MINUTE: 200, - - // status codes - SUCCESS: 200, - NOT_FOUND: 404, - SERVER_ERROR: 500, - - POST_NOT_FOUND_MESSAGE: `Stay tuned, we have some exciting posts in the works that we'll be sharing with you shortly.`, - SEO_META_DATA: { - title: - "Web and Mobile app development resources to help you make an informed decision", - description: - "Explore our Web and Mobile app development resources to equip yourself with the software development process.", - authorName: "canopas", - }, - - SHOW_DRAFT_POSTS: false, - SHOW_SEARCH_POSTS: false, - SHOW_CATEGORY_POSTS: false, - SHOW_HEADER_TITLE: false, - SHOW_COMMENT_SECTION: false, - SHOW_NEW_CONTENT: false, - - FACEBOOK_URL: "https://www.facebook.com/canopassoftware", - INSTAGRAM_URL: "https://www.instagram.com/canopassoftware/", - TWITTER_URL: "https://twitter.com/canopassoftware", - BLOG_URL: "https://blog.canopas.com/", - LINKEDIN_URL: "https://www.linkedin.com/company/canopasinc", - YOUTUBE_URL: - "https://www.youtube.com/channel/UC77VyeTVJ45HiUS_o2GNcBA/videos", -}); diff --git a/website/config.js b/website/config.js deleted file mode 100644 index c9157cec..00000000 --- a/website/config.js +++ /dev/null @@ -1,35 +0,0 @@ -export default Object.freeze({ - STRAPI_URL: process.env.NEXT_PUBLIC_STRAPI_URL, - WEBSITE_URL: process.env.NEXT_PUBLIC_WEBSITE_URL, - API_BASE: process.env.NEXT_PUBLIC_API_BASE, - - WORDS_PER_MINUTE: 200, - - // status codes - SUCCESS: 200, - NOT_FOUND: 404, - SERVER_ERROR: 500, - - POST_NOT_FOUND_MESSAGE: `Stay tuned, we have some exciting posts in the works that we'll be sharing with you shortly.`, - SEO_META_DATA: { - title: "Software Solutions & Trends | Canopas Blog", - description: - "Canopas blogs will help you to become a better software developer. We are sharing knowledge on Web, Backend, iOS, Android, and Flutter development", - authorName: "canopas", - }, - - SHOW_DRAFT_POSTS: true, - SHOW_SEARCH_POSTS: false, - SHOW_CATEGORY_POSTS: false, - SHOW_HEADER_TITLE: false, - SHOW_COMMENT_SECTION: false, - SHOW_NEW_CONTENT: true, - - FACEBOOK_URL: "https://www.facebook.com/canopassoftware", - INSTAGRAM_URL: "https://www.instagram.com/canopassoftware/", - TWITTER_URL: "https://twitter.com/canopassoftware", - BLOG_URL: "https://blog.canopas.com/", - LINKEDIN_URL: "https://www.linkedin.com/company/canopasinc", - YOUTUBE_URL: - "https://www.youtube.com/channel/UC77VyeTVJ45HiUS_o2GNcBA/videos", -}); diff --git a/website/lib/category.js b/website/lib/category.js deleted file mode 100644 index 61553e23..00000000 --- a/website/lib/category.js +++ /dev/null @@ -1,22 +0,0 @@ -import config from "../config"; -const STRAPI_URL = config.STRAPI_URL; - -// Get categories -async function fetchCategory(slug) { - const requestUrl = slug - ? `${STRAPI_URL}/v1/categories/${slug}?populate=deep` - : `${STRAPI_URL}/v1/categories?populate=deep`; - - try { - const response = await fetch(requestUrl, { - method: "GET", - }); - const data = await response.json(); - return [response.status, data]; - } catch (err) { - console.log(err); - return [config.SERVER_ERROR, null]; - } -} - -export { fetchCategory }; diff --git a/website/lib/user.js b/website/lib/user.js deleted file mode 100644 index 39d9264a..00000000 --- a/website/lib/user.js +++ /dev/null @@ -1,22 +0,0 @@ -import config from "../config"; -const STRAPI_URL = config.STRAPI_URL; - -// Get author -async function fetchUser(slug) { - const requestUrl = slug - ? `${STRAPI_URL}/v1/users/${slug}?populate=deep` - : `${STRAPI_URL}/v1/users?populate=deep`; - - try { - const response = await fetch(requestUrl, { - method: "GET", - }); - const data = await response.json(); - return [response.status, data]; - } catch (err) { - console.log(err); - return [config.SERVER_ERROR, null]; - } -} - -export { fetchUser }; diff --git a/website/next.config.js b/website/next.config.js deleted file mode 100644 index 9092b052..00000000 --- a/website/next.config.js +++ /dev/null @@ -1,19 +0,0 @@ -const path = require("path"); - -module.exports = { - basePath: "/resources", - output: "standalone", - reactStrictMode: true, - sassOptions: { - includePaths: [path.join(__dirname, "assets")], - }, - images: { - unoptimized: true, - remotePatterns: [ - { - protocol: "https", - hostname: process.env.NEXT_PUBLIC_STRAPI_DOMAIN, - }, - ], - }, -}; diff --git a/website/package.json b/website/package.json deleted file mode 100644 index 9089d0c8..00000000 --- a/website/package.json +++ /dev/null @@ -1,45 +0,0 @@ -{ - "name": "next-blog", - "version": "0.1.0", - "private": true, - "scripts": { - "dev": "next dev", - "build": "next build", - "start": "next start", - "lint": "prettier -c . && next lint" - }, - "dependencies": { - "@google-cloud/recaptcha-enterprise": "^3.4.1", - "@headlessui/react": "^1.7.4", - "@next/font": "^13.4.19", - "@tailwindcss/line-clamp": "^0.4.2", - "axios": "^1.2.2", - "highlight.js": "^11.7.0", - "isomorphic-dompurify": "^1.8.0", - "markdown-it": "^13.0.1", - "mixpanel-browser": "^2.47.0", - "next": "^13.4.9", - "node-sass": "^9.0.0", - "react": "18.2.0", - "react-dom": "18.2.0", - "react-google-recaptcha": "^3.1.0", - "sass": "^1.63.6", - "sharp": "^0.32.2", - "smoothscroll-polyfill": "^0.4.4", - "swiper": "^10.0.4" - }, - "devDependencies": { - "@fortawesome/fontawesome-svg-core": "^6.2.1", - "@fortawesome/free-brands-svg-icons": "^6.4.0", - "@fortawesome/free-regular-svg-icons": "^6.2.1", - "@fortawesome/free-solid-svg-icons": "^6.2.1", - "@fortawesome/react-fontawesome": "^0.2.0", - "@tailwindcss/aspect-ratio": "^0.4.2", - "@tailwindcss/typography": "^0.5.8", - "autoprefixer": "^10.4.14", - "eslint": "8.44.0", - "eslint-config-next": "13.4.9", - "postcss": "^8.4.26", - "tailwindcss": "^3.3.3" - } -} diff --git a/website/pages/404.js b/website/pages/404.js deleted file mode 100644 index a503ee00..00000000 --- a/website/pages/404.js +++ /dev/null @@ -1,43 +0,0 @@ -import Link from "next/link"; -import Image from "next/image"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { faArrowLeft } from "@fortawesome/free-solid-svg-icons"; -import firstErrorLetter from "../assets/images/404page_4_1.svg"; -import middleErrorLetter from "../assets/images/icon.svg"; -import lastErrorLetter from "../assets/images/404page_4_2.svg"; - -export default function Custom404() { - return ( - <> -
-
- 404 - 404 - 404 -
-
- The page you’re looking for was moved, renamed or might never existed. -
-
- - - - Back to Home page - - -
-
- - ); -} diff --git a/website/pages/[slug].js b/website/pages/[slug].js deleted file mode 100644 index 7db2b958..00000000 --- a/website/pages/[slug].js +++ /dev/null @@ -1,578 +0,0 @@ -import React, { useState, useRef, useEffect, useLayoutEffect } from "react"; -import Image from "next/image"; -import dynamic from "next/dynamic"; -import Link from "next/link"; -import Script from "next/script"; -import axios from "axios"; -import config from "../config"; -import Seo from "./seo"; -import NotFound from "./404"; -import { setPostFields, filterPostsByCategoryAndTag } from "../utils"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { faClock } from "@fortawesome/free-regular-svg-icons"; -import { - faTags, - faLink, - faXmark, - faArrowUpRightFromSquare, -} from "@fortawesome/free-solid-svg-icons"; -import { - faFacebook, - faTwitter, - faReddit, - faLinkedinIn, -} from "@fortawesome/free-brands-svg-icons"; -import hljs from "highlight.js/lib/common"; - -const CTA1 = dynamic(() => import("../components/cta/CTA1"), { ssr: false }); -const CTA2 = dynamic(() => import("../components/cta/CTA2"), { ssr: false }); -const CTA3 = dynamic(() => import("../components/cta/CTA3"), { ssr: false }); -const CTA4 = dynamic(() => import("../components/cta/CTA4"), { ssr: false }); -const CTA5 = dynamic(() => import("../components/cta/CTA5"), { ssr: false }); -const Comment = dynamic(() => import("../components/comments/index"), { - ssr: false, -}); -const recommendedPosts = dynamic( - () => import("../components/posts/recommendedPosts"), - { ssr: false }, -); -const AuthorDetails = dynamic(() => import("../components/authorDetails"), { - ssr: false, -}); - -export async function getServerSideProps(context) { - const slug = context.params.slug; - let response, - postData = null; - let posts = []; - - try { - response = await axios.get( - config.STRAPI_URL + "/v1/posts/" + slug + "?populate=deep", - ); - postData = response.data.data; - setPostFields(postData); - } catch (err) { - response = err.response; - } - - const status = response ? response.status : config.NOT_FOUND; - - // fetch posts other than this post by category name - let published = config.SHOW_DRAFT_POSTS - ? "&publicationState=preview" - : "&publicationState=live"; - try { - response = await axios.get( - config.STRAPI_URL + "/v1/posts?filters[slug][$ne]=" + slug + published, - ); - posts = response.data.data; - posts.forEach((post) => setPostFields(post)); - } catch (err) { - console.log(err); - } - - context.res.setHeader( - "Cache-Control", - "public, s-maxage=10, stale-while-revalidate=59", - ); - - return { props: { postData, status, posts } }; -} - -export default function Post({ postData, status, posts, mixpanel }) { - const [loaded, setLoaded] = useState(false); - const [activeId, setActiveId] = useState(null); - const [alerts, setAlerts] = useState(false); - const [message, setMessage] = useState(""); - const [headerHeight, setHeaderHeight] = useState(""); - const contentRef = useRef(null); - let relatedPosts = []; - let firstHeadingId; - - setTimeout(() => { - setLoaded(true); - }, 50); - - if (alerts) { - setTimeout(() => { - setAlerts(false); - }, 2000); - } - - if (!postData) { - status = config.NOT_FOUND; - } - - let post = postData?.attributes; - let CTAData = post?.cta.data; - let published_on = post?.published_on.replace(",", ""); - let published_time = new Date(post?.publishedAt).toLocaleTimeString(); - - if (post?.published_on == "Draft" && !config.SHOW_DRAFT_POSTS) { - status = config.NOT_FOUND; - } - - let blogContent = config.SHOW_NEW_CONTENT - ? post?.new_content || post?.content - : post?.content; - let indexContent = config.SHOW_NEW_CONTENT - ? post?.new_toc || post?.toc - : post?.toc; - - blogContent = blogContent - ?.replace( - / { - let match2 = /#([^\n]+-0)\b/g.exec(href); - - let classes = - "text-ellipsis hover:bg-gradient-to-r from-pink-300 to-orange-300 hover:text-transparent hover:bg-clip-text"; - if (href === activeId) { - classes += - " relative bg-gradient-to-r bg-clip-text text-transparent after:absolute after:left-0 after:bottom-0 after:w-full after:h-px after:bg-gradient-to-r"; - } else if (match2 && match2[1]) { - firstHeadingId = match2[1].replace("#", ""); - } - - return ` { - event.preventDefault(); - let linkHref = event.target.getAttribute("href"); - let element = contentRef.current.querySelector(linkHref); - if (element) { - window.scrollTo({ - top: element.offsetTop - headerHeight, - behavior: "smooth", - }); - } - }; - relatedPosts = filterPostsByCategoryAndTag(post, posts); - - const handleScroll = () => { - if (contentRef.current) { - const headers = contentRef.current.querySelectorAll("h1, h2"); - headers.forEach((header, index) => { - const documentHeight = document.body.scrollHeight; - const currentScroll = window.scrollY + window.innerHeight; - if (currentScroll > documentHeight) { - setActiveId("#" + header.id); - } else { - if (header.offsetTop - window.pageYOffset <= 200) { - setActiveId("#" + header.id); - } - } - }); - } - }; - - const copyLink = () => { - mixpanel.track("tap_copy_link"); - const el = document.createElement("input"); - el.value = window.location.href; - document.body.appendChild(el); - el.select(); - document.execCommand("copy"); - document.body.removeChild(el); - setAlerts(true); - setMessage("Link Copied"); - }; - - const shareBlog = async () => { - mixpanel.track("tap_share_mobile"); - try { - await navigator.share({ - title: post.title, - text: post.meta_description, - url: config.WEBSITE_URL + "/resources/" + post.slug, - }); - } catch (err) {} - }; - - useEffect(() => { - if (postData) { - hljs.highlightAll(); - setLoaded(false); - - let element = document.getElementById(firstHeadingId); - if (element) { - element.style.marginTop = "0"; - } - - setHeaderHeight( - document.getElementsByTagName("header")["0"].clientHeight, - ); - - document.querySelectorAll("oembed[url]").forEach((element) => { - if ( - typeof iframely !== "undefined" && - !element.getAttribute("data-loaded") - ) { - iframely.load(element, element.attributes.url.value); - element.setAttribute("data-loaded", true); - } - }); - window.addEventListener("scroll", handleScroll); - return () => { - window.removeEventListener("scroll", handleScroll); - }; - } - }, [blogContent, firstHeadingId, postData]); - - useLayoutEffect(() => { - import("smoothscroll-polyfill").then(({ default: smoothscroll }) => { - smoothscroll.polyfill(); - }); - - window.__forceSmoothScrollPolyfill__ = true; - }, []); - - return ( - <> - - - Loading

}> -
- -