diff --git a/.gitignore b/.gitignore index ac12af4..533853d 100644 --- a/.gitignore +++ b/.gitignore @@ -29,6 +29,9 @@ pnpm-lock.yaml .next/ out/ build + +# Wrangler local dev state (auto-generated by wrangler pages dev) +.wrangler/ dist dist-ssr diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..521a9f7 --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +legacy-peer-deps=true diff --git a/CHANGELOG.md b/CHANGELOG.md index 580d6ed..750659c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,11 @@ +# [1.4.0-develop.10](https://github.com/betterbugs/dev-tools/compare/v1.4.0-develop.9...v1.4.0-develop.10) (2026-05-22) + + +### Features + +* add class-validator dependency and update page styles ([860e1f5](https://github.com/betterbugs/dev-tools/commit/860e1f55497c9ac86a70fadbc6725a5d07f3f26c)) +* add Image Resizer and Time Calculator components ([35c3e79](https://github.com/betterbugs/dev-tools/commit/35c3e7901069e60a62e44a9987298945f18f65cc)) + # [1.4.0-develop.9](https://github.com/betterbugs/dev-tools/compare/v1.4.0-develop.8...v1.4.0-develop.9) (2026-04-14) ### Features diff --git a/README.md b/README.md index abef891..50d0b48 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,7 @@ With BetterBugs, debugging stops being guesswork and becomes a shared, reliable source of truth. **Key Features:** + - Real session context from production environments - One-click bug capture with automatic context gathering - Reproducible bug timelines with clear event sequences @@ -92,12 +93,14 @@ BetterBugs Development Tools solves this by providing: ## Available Tools ### Text Tools + - Text case converters (uppercase, lowercase, title case, sentence case) - Text counters (word, character, sentence, line) - Text formatters and manipulators - Lorem Ipsum generator ### Code Tools + - JavaScript minifier and obfuscator - JSON formatter and minifier - HTML to Markdown converter @@ -106,6 +109,7 @@ BetterBugs Development Tools solves this by providing: - CSS formatters and converters ### Data Tools + - JSON validator and formatter - CSV converters and tools - QR Code generator @@ -114,6 +118,7 @@ BetterBugs Development Tools solves this by providing: - URL encoder/decoder ### Color Tools + - Color code converters (HEX, RGB, CMYK) - Color picker and inverter - Random color generator @@ -132,17 +137,20 @@ Browse all available tools at [BetterBugs Development Tools](https://www.betterb ### Installation 1. **Clone the repository** + ```bash git clone https://github.com/betterbugs/dev-tools.git cd dev-tools ``` 2. **Install dependencies** + ```bash npm install ``` 3. **Run the development server** + ```bash npm run dev ``` @@ -190,18 +198,19 @@ dev-tools/ 4. The tool automatically appears on the homepage Example: + ```typescript "use client"; import React, { useState } from "react"; const MyNewTool = () => { const [input, setInput] = useState(""); - + return (
- setInput(e.target.value)} + setInput(e.target.value)} /> {/* Your tool logic */}
@@ -216,6 +225,7 @@ export default MyNewTool; We welcome contributions! Please read our [Contributing Guide](CONTRIBUTING.md) to get started. Key points: + - Follow [Conventional Commits](https://www.conventionalcommits.org) - Target the `develop` branch for feature PRs - See our [Pull Request Template](.github/PULL_REQUEST_TEMPLATE.md) for guidance @@ -232,6 +242,7 @@ Found a security vulnerability? Please email us at dev@betterbugs.io instead of ## Reporting Issues Found a bug? Open an issue on [GitHub Issues](https://github.com/betterbugs/dev-tools/issues) with: + - Clear description of the problem - Steps to reproduce - Expected vs actual behavior diff --git a/app/ClientLayout.tsx b/app/ClientLayout.tsx new file mode 100644 index 0000000..dfec56d --- /dev/null +++ b/app/ClientLayout.tsx @@ -0,0 +1,75 @@ +'use client'; +import { Suspense, useContext } from 'react'; +import { CookiesProvider } from 'react-cookie'; +import { + LayoutContext, + LayoutContextModel, + LayoutContextProvider, +} from './contexts/layoutContexts'; +import { ThemeProvider } from './contexts/themeContext'; +import HeaderComponent from './components/layout/Header/headerComponent'; +import FooterComponent from './components/layout/footer/footerComponent'; +import CanonicalLink from './components/theme/canonicalLink/canonicalLink'; +import { useMediaQuery } from 'react-responsive'; +import dynamic from 'next/dynamic'; + +const AnimatedCursor = dynamic(() => import('react-animated-cursor'), { + ssr: false, +}); + +const InnerContent = ({ children }: { children: React.ReactNode }) => { + const { isClient }: LayoutContextModel = useContext(LayoutContext); + const isDesktopOrLaptop = useMediaQuery({ query: '(min-width: 1024px)' }); + + return ( +
+ {isClient && isDesktopOrLaptop && ( + + )} + + + + {children} + +
+ ); +}; + +const ClientLayout = ({ children }: { children: React.ReactNode }) => { + return ( + + + + + + {children} + + + + + ); +}; + +export default ClientLayout; diff --git a/app/[slug]/PageClient.tsx b/app/[slug]/PageClient.tsx new file mode 100644 index 0000000..50187c9 --- /dev/null +++ b/app/[slug]/PageClient.tsx @@ -0,0 +1,1220 @@ +'use client'; +import React, { useEffect, useState } from 'react'; +import { DEVELOPMENTTOOLS } from '@/app/libs/developmentToolsConstant'; +import Link from 'next/link'; +import { Button } from 'antd'; +import Image from 'next/image'; +import StarGardientIcon from '@/app/components/theme/Icon/starGradientIcon'; +import { + developmentToolsRoutes, + Extension_URL, + integrationTools, +} from '@/app/libs/constants'; +import PlayIcon from '@/app/components/theme/Icon/playIcon'; +import LogsGradientIcon from '@/app/components/theme/Icon/logsGradientIcon'; +import ShareGradientIcon from '@/app/components/theme/Icon/shareGradientIcon'; +import PowerCircleIcon from '@/app/components/theme/Icon/powerCircleIcon'; +import ComparisonsStyles from '../components/comparisonsComponent/comparisonsStyles.module.scss'; +import DevelopmentToolsStyles from '../developmentToolsStyles.module.scss'; +import { usePathname } from 'next/navigation'; +import RecorderGradientIcon from '@/app/components/theme/Icon/recorderGradientIcon'; +import SEOComponent from '@/app/components/theme/SEOComponent/SEOComponent'; +import { detectBrowser } from '@/app/libs/helpers'; +import EdgeIcon from '@/app/components/theme/Icon/edgeIcon'; +import { useTheme } from '@/app/contexts/themeContext'; + +const PageClient = ({ slug }: { slug: string }) => { + const [browser, setBrowser] = useState('chrome'); + const { isLightTheme } = useTheme(); + const logoSrc = isLightTheme + ? '/images/bb-logo-light.svg' + : '/images/bb-logo.svg'; + + useEffect(() => { + setBrowser(detectBrowser()); + }, []); + const toolsData = DEVELOPMENTTOOLS[slug]; + const { + hero_section, + development_tools_list, + development_tools_about_details, + development_tools_what, + development_tools_user_agent_info, + development_tools_steps_guide, + development_tools_how_use, + development_tools_Comparison, + development_tool_example, + meta_data, + } = toolsData; + const pathname = usePathname(); + + const currentTool = developmentToolsRoutes.find( + (tool: any) => tool.path === pathname + ); + + return ( + <> + +
+
+ {/*Hero cta section */} +
+
+
+ {/* Left Section */} +
+ BetterBugs Logo +
+

+ The essential AI{' '} + companion every QA needs +

+ + + +
+ +

+ Swiftly document and share bugs like never before +

+
+ + + +
+

+ Free Forever. No Credit Card Required. +

+
+ + {/* Right Section */} +
+ {/* Features Section */} +
+
+ +

+ Use AI to + reproduce and fix bugs +

+
+
+ +

+ + Screen Recording + {' '} + with{' '} + + Rewind (Upto 2-min) + +

+
+
+ +

+ Capture{' '} + + {' '} + backend logs + + with every bug report +

+
+
+ +

+ Assign bugs and{' '} + share at + single click +

+
+
+ +
+
+
+
+
+
+ +
+ {/* Heading section */} + {hero_section && ( +
+
+

+ {hero_section?.title} +

+

+ {hero_section?.description + ?.split('BetterBugs.io') + .map((part: any, index: any, arr: any) => ( + + {part} + {index !== arr.length - 1 && ( + + BetterBugs.io + + )} + + ))} +

+
+
+ )} + + {/* Integrated Tool section */} + {currentTool ? currentTool?.component : null} + + {/* Tool description section */} +
+
+ {/* Tools Panel - 20% width, fixed */} + {development_tools_list?.length > 0 && ( +
+
+
+

+ Other Tools +

+
+ {development_tools_list?.map((tool: any, index: any) => ( + +
+ {tool?.tool} +
+ + ))} +
+
+ )} + + {/* Q&A Panel - 80% width, scrollable */} +
+ {/* about section */} +
+ {development_tools_about_details?.about_title && ( +

+ {development_tools_about_details?.about_title} +

+ )} + {development_tools_about_details?.about_description?.map( + (desc: any, index: number) => { + // Check if this description item has a list + if (desc?.list && Array.isArray(desc?.list)) { + return ( +
    + {desc?.list?.map( + (listItem: any, listIndex: number) => ( +
  • + {listItem?.title} + {listItem?.description && ( + + {listItem?.description} + + )} +
  • + ) + )} +
+ ); + } + + // Check if this description item has examples + if (desc?.example && Array.isArray(desc?.example)) { + return ( +
+ {/* Render description if it exists */} + {desc?.description && ( +

+ {desc.description} +

+ )} + {/* Render examples */} + {desc.example.map( + (exampleItem: any, exampleIndex: number) => { + const isExampleHeading = + exampleItem?.example_input?.startsWith( + 'Example' + ); + const isInputOrOutput = + exampleItem?.example_input === 'Input' || + exampleItem?.example_input === 'Output'; + const isStringOutput = + typeof exampleItem?.example_output === + 'string'; + const isArrayOutput = Array.isArray( + exampleItem?.example_output + ); + + return ( +
0 ? 'mt-3' : ''} + > + {isExampleHeading && ( + <> +

+ {exampleItem.example_input} +

+ {isStringOutput && ( +

+ {exampleItem.example_output} +

+ )} + + )} + {isInputOrOutput && ( + <> +

+ {exampleItem.example_input} +

+ {isArrayOutput && ( +
+ {exampleItem.example_output.map( + ( + outputItem: any, + outputIndex: number + ) => ( +

+ {outputItem?.value} +

+ ) + )} +
+ )} + + )} +
+ ); + } + )} +
+ ); + } + + // Handle regular description text + const descriptions = desc?.description || ''; + const splitDescriptions = + descriptions.split(/((['"]).*?\2)/); // Split quoted and unquoted text + + return ( +

+ {splitDescriptions.map( + (text: any, subIndex: any) => { + const isQuoted = + typeof text === 'string' && + /^(['"]).*\1$/.test(text); + const containsBetterBugs = + text.includes('BetterBugs.io'); + if (containsBetterBugs) { + const parts = text.split('BetterBugs.io'); + return ( + + + {parts[0]} + + + BetterBugs.io + + + {parts[1]} + + + ); + } + + return ( + + {text} + + ); + } + )} +

+ ); + } + )} + + {development_tools_about_details?.placeholder && ( +
    + {development_tools_about_details?.placeholder?.map( + (placeholder: any, index: any) => ( +
  • + + {placeholder?.title} + +
  • + ) + )} +
+ )} +
+ + {/* user agent info section */} + {development_tools_user_agent_info && ( +
+ {development_tools_user_agent_info?.info_title && ( +

+ {development_tools_user_agent_info?.info_title} +

+ )} + {development_tools_user_agent_info?.intro_text && ( +

+ {development_tools_user_agent_info?.intro_text} +

+ )} + {development_tools_user_agent_info?.example_string && ( +

+ {development_tools_user_agent_info?.example_string} +

+ )} + {development_tools_user_agent_info?.example_string_description && ( +

+ { + development_tools_user_agent_info?.example_string_description + } +

+ )} + {development_tools_user_agent_info?.info_items && ( +
+
    + {development_tools_user_agent_info?.info_items?.map( + (item: any, index: number) => ( +
  • + + {item?.part}{' '} + + + {item?.description} + +
  • + ) + )} +
+
+ )} +
+ )} + + {/* step-by-step guide */} +
+ {development_tools_steps_guide?.guide_title && ( + <> +
+ {development_tools_steps_guide?.guide_title} +
+

+ {development_tools_steps_guide?.guide_description + ?.split(/((['"]).*?\2)/g) + ?.map((parts: any, i: any) => { + const isQuoted = + typeof parts === 'string' && + /^(['"]).*\1$/.test(parts); + return isQuoted ? ( + + {parts} + + ) : ( + + {parts} + + ); + })} +

+ + )} + {development_tools_steps_guide?.steps?.length > 0 && ( +
+ {development_tools_steps_guide?.steps?.map( + (guide: any, index: any) => { + const description = guide?.step_description; + const description2 = guide?.step_description2; + + // Split quoted and unquoted text + const parts = description?.split(/((['"]).*?\2)/); + const desParts = + description2?.split(/((['"]).*?\2)/); + + return ( +
+ {/* Primary Flex Layout for Step Key, Title, and Description */} +
+ + {guide?.step_key} + + + {guide?.step_title} + +
+ + {/* Step descriptions (below title) */} + {description && ( +
+ {parts?.map((part: any, i: any) => { + const isQuoted = + typeof part === 'string' && + /^(['"]).*\1$/.test(part); + return isQuoted ? ( + + {part} + + ) : ( + + {part} + + ); + })} +
+ )} + + {/* Step Description2 on a New Line, Only If Present */} + {description2 && desParts?.length > 0 && ( +
+ {desParts?.map((part: any, i: any) => { + const isQuoted = + typeof part === 'string' && + /^(['"]).*\1$/.test(part); + return isQuoted ? ( + + {part} + + ) : ( + + {part} + + ); + })} +
+ )} + + {guide?.steps_points?.length > 0 && ( +
    + {guide?.steps_points?.map( + (p: any, index: number) => ( +
  • + {p?.steps_points_title && ( + + {p?.steps_points_title} + + )} + {p?.steps_points_description && ( +

    + {p?.steps_points_description + ?.split(/((['"]).*?\2)/) + .map( + (part: string, i: number) => + typeof part === 'string' && + /^(['"]).*\1$/.test( + part + ) ? ( + + {part} + + ) : ( + + {part + .split( + /(\/\/.*?\/\/)/ + ) + .map( + ( + sub: string, + j: number + ) => + sub.startsWith( + '//' + ) && + sub.endsWith( + '//' + ) ? ( + + {sub.slice( + 2, + -2 + )} + + ) : ( + sub + ) + )} + + ) + )} +

    + )} + {Array.isArray(p?.steps_subpoint) && + p?.steps_subpoint?.length > 0 && ( +
      + {p?.steps_subpoint?.map( + ( + sub_p: any, + subIndex: number + ) => ( +
    • + {sub_p?.title && ( + + {sub_p?.title} + + )} + {sub_p?.description && ( + + {sub_p?.description + ?.split( + /((['"]).*?\2)/ + ) + .map( + ( + part: string, + i: number + ) => + typeof part === + 'string' && + /^(['"]).*\1$/.test( + part + ) ? ( + + {part} + + ) : ( + + {part + .split( + /(\/\/.*?\/\/)/ + ) + .map( + ( + sub: string, + j: number + ) => + sub.startsWith( + '//' + ) && + sub.endsWith( + '//' + ) ? ( + + {sub.slice( + 2, + -2 + )} + + ) : ( + sub + ) + )} + + ) + )} + + )} +
    • + ) + )} +
    + )} +
  • + ) + )} +
+ )} +
+ ); + } + )} +
+ )} +
+ + {/* example section */} + {development_tool_example && ( +
+ {development_tool_example?.example_title && ( +

+ {development_tool_example?.example_title} +

+ )} + {development_tool_example?.example_description && ( +

+ {development_tool_example?.example_description} +

+ )} + + {development_tool_example?.example_input && ( +
+ {development_tool_example?.example_input?.title && ( +

+ {development_tool_example?.example_input?.title} +

+ )} + {development_tool_example?.example_input + ?.json_data && ( +
+                              
+                                {
+                                  development_tool_example?.example_input
+                                    ?.json_data
+                                }
+                              
+                            
+ )} +
+ )} + + {development_tool_example?.example_outputs && ( +
+ {development_tool_example?.example_outputs?.intro && ( +

+ {development_tool_example?.example_outputs?.intro} +

+ )} + {development_tool_example?.example_outputs?.outputs?.map( + (output: any, index: number) => ( +
+ {output?.mode && ( +

+ {output?.mode} +

+ )} + {output?.title && ( +

+ {output?.title} +

+ )} + {output?.content && ( +
+                                    
+                                      {output?.content}
+                                    
+                                  
+ )} + {output?.note && ( +

+ {output?.note} +

+ )} +
+ ) + )} +
+ )} + + {/* JavaScript example section (optional) */} + {development_tool_example?.javascript_example && ( +
+ {development_tool_example?.javascript_example + ?.title && ( +
+ { + development_tool_example?.javascript_example + ?.title + } +
+ )} + {development_tool_example?.javascript_example + ?.description && ( +

+ { + development_tool_example?.javascript_example + ?.description + } +

+ )} + + {development_tool_example?.javascript_example?.methods + ?.length > 0 && ( +
+
    + {development_tool_example?.javascript_example?.methods?.map( + (method: any, index: number) => ( +
  • + {method?.name && ( + + {method?.name}{' '} + + )} + {method?.description && ( + + {method?.description} + + )} +
  • + ) + )} +
+
+ )} + + {development_tool_example?.javascript_example + ?.examples?.length > 0 && ( +
+ {development_tool_example?.javascript_example?.examples?.map( + (example: any, index: number) => ( +
+ {example?.title && ( +

+ {example?.title} +

+ )} + {example?.code && ( +
+                                        
+                                          {example?.code}
+                                        
+                                      
+ )} +
+ ) + )} +
+ )} + + {development_tool_example?.javascript_example + ?.note && ( +

+ { + development_tool_example?.javascript_example + ?.note + } +

+ )} +
+ )} +
+ )} + + {/* what section */} + {development_tools_what && ( +
+ {development_tools_what?.about_title && ( +

+ {development_tools_what?.about_title} +

+ )} + {development_tools_what?.what_description?.map( + (desc: any, index: number) => { + const descriptions = desc?.descriptions; + const splitDescriptions = + descriptions.split(/((['"]).*?\2)/); // Split quoted and unquoted text + + return ( +

+ {splitDescriptions.map( + (text: any, subIndex: any) => { + const isQuoted = + typeof text === 'string' && + /^(['"]).*\1$/.test(text); + + return ( + + {text} + + ); + } + )} +

+ ); + } + )} +
+ )} + + {/* how to use */} +
+ {development_tools_how_use?.how_use_title && ( + <> +
+ {development_tools_how_use?.how_use_title} +
+

+ {development_tools_how_use?.how_use_description} +

+ + )} + {development_tools_how_use?.point?.length > 0 && ( +
+
    + {development_tools_how_use?.point?.map( + (how: any, index: any) => + how?.heading ? ( +

    + {how?.heading} +

    + ) : ( +
  • + <> + + {how?.title} + + + {how?.description} + + +
  • + ) + )} +
+
+ )} +
+ + {/* Comparison section */} + {development_tools_Comparison && ( +
+ {development_tools_Comparison?.title && ( +

+ {development_tools_Comparison?.title} +

+ )} + {Array.isArray( + development_tools_Comparison?.description + ) && + development_tools_Comparison?.description.map( + (d: any, index: any) => ( +

+ {d?.desc} +

+ ) + )} +
+ )} +
+
+
+ + {/* cta section */} +
+
+
+ {/* Left Section */} +
+ BetterBugs Logo +
+

+ The essential AI{' '} + companion every QA needs +

+ + + +
+ +

+ Swiftly document and share bugs like never before +

+
+ + + + + + +
+

+ Free Forever. No Credit Card Required. +

+
+ + {/* Right Section */} +
+
+ + {/* Features Section */} +
+
+ +

+ Use AI{' '} + to reproduce and fix bugs +

+
+
+ +

+ + Screen Recording + {' '} + with{' '} + + Rewind (Upto 2-min) + +

+
+
+ +

+ Capture{' '} + + {' '} + backend logs + + with every bug report +

+
+
+ +

+ Assign bugs and{' '} + share at + single click +

+
+
+ +
+
+
+ + {/* Integrations Section */} +
+

+ Integrations +

+

+ Two-way sync with +
popular tools +

+
+ {integrationTools?.map((tool, index) => ( + +
+ + {tool?.icon} + +

+ {tool?.name} +

+
+ + ))} +
+
+
+
+
+
+
+
+ + ); +}; + +export default PageClient; diff --git a/app/[slug]/page.tsx b/app/[slug]/page.tsx index e469e6c..8f08f40 100644 --- a/app/[slug]/page.tsx +++ b/app/[slug]/page.tsx @@ -1,1143 +1,13 @@ -'use client'; -import React, { useEffect, useState } from 'react'; import { DEVELOPMENTTOOLS } from '@/app/libs/developmentToolsConstant'; -import Link from 'next/link'; -import { Button } from 'antd'; -import Image from 'next/image'; -import StarGardientIcon from '@/app/components/theme/Icon/starGradientIcon'; -import { - developmentToolsRoutes, - Extension_URL, - integrationTools, -} from '@/app/libs/constants'; -import PlayIcon from '@/app/components/theme/Icon/playIcon'; -import LogsGradientIcon from '@/app/components/theme/Icon/logsGradientIcon'; -import ShareGradientIcon from '@/app/components/theme/Icon/shareGradientIcon'; -import PowerCircleIcon from '@/app/components/theme/Icon/powerCircleIcon'; -import ComparisonsStyles from '../components/comparisonsComponent/comparisonsStyles.module.scss'; -import DevelopmentToolsStyles from '../developmentToolsStyles.module.scss'; -import { usePathname } from 'next/navigation'; -import RecorderGradientIcon from '@/app/components/theme/Icon/recorderGradientIcon'; -import SEOComponent from '@/app/components/theme/SEOComponent/SEOComponent'; -import { detectBrowser } from '@/app/libs/helpers'; -import EdgeIcon from '@/app/components/theme/Icon/edgeIcon'; -import { useTheme } from '@/app/contexts/themeContext'; +import PageClient from './PageClient'; -const Page = ({ params: { slug } }: { params: { slug: string } }) => { - const [browser, setBrowser] = useState('chrome'); - const { isLightTheme } = useTheme(); - const logoSrc = isLightTheme - ? '/images/bb-logo-light.svg' - : '/images/bb-logo.svg'; +export async function generateStaticParams() { + return Object.keys(DEVELOPMENTTOOLS).map(slug => ({ slug })); +} - useEffect(() => { - setBrowser(detectBrowser()); - }, []); - const toolsData = DEVELOPMENTTOOLS[slug]; - const { - hero_section, - development_tools_list, - development_tools_about_details, - development_tools_what, - development_tools_user_agent_info, - development_tools_steps_guide, - development_tools_how_use, - development_tools_Comparison, - development_tool_example, - meta_data, - } = toolsData; - const pathname = usePathname(); - - const currentTool = developmentToolsRoutes.find( - (tool: any) => tool.path === pathname - ); - - return ( - <> - -
-
- {/*Hero cta section */} -
-
-
- {/* Left Section */} -
- BetterBugs Logo -
-

- The essential AI{' '} - companion every QA needs -

- - - -
- -

- Swiftly document and share bugs like never before -

-
- - - -
-

- Free Forever. No Credit Card Required. -

-
- - {/* Right Section */} -
- {/* Features Section */} -
-
- -

- Use AI to - reproduce and fix bugs -

-
-
- -

- - Screen Recording - {' '} - with{' '} - - Rewind (Upto 2-min) - -

-
-
- -

- Capture{' '} - - {' '} - backend logs - - with every bug report -

-
-
- -

- Assign bugs and{' '} - share at - single click -

-
-
- -
-
-
-
-
-
- -
- {/* Heading section */} - {hero_section && ( -
-
-

- {hero_section?.title} -

-

- {hero_section?.description - ?.split('BetterBugs.io') - .map((part: any, index: any, arr: any) => ( - - {part} - {index !== arr.length - 1 && ( - - BetterBugs.io - - )} - - ))} -

-
-
- )} - - {/* Integrated Tool section */} - {currentTool ? currentTool?.component : null} - - {/* Tool description section */} -
-
- {/* Tools Panel - 20% width, fixed */} - {development_tools_list?.length > 0 && ( -
-
-
-

- Other Tools -

-
- {development_tools_list?.map((tool: any, index: any) => ( - -
- {tool?.tool} -
- - ))} -
-
- )} - - {/* Q&A Panel - 80% width, scrollable */} -
- {/* about section */} -
- {development_tools_about_details?.about_title && ( -

- {development_tools_about_details?.about_title} -

- )} - {development_tools_about_details?.about_description?.map( - (desc: any, index: number) => { - // Check if this description item has a list - if (desc?.list && Array.isArray(desc?.list)) { - return ( -
    - {desc?.list?.map( - (listItem: any, listIndex: number) => ( -
  • - {listItem?.title} - {listItem?.description && ( - - {listItem?.description} - - )} -
  • - ) - )} -
- ); - } - - // Check if this description item has examples - if (desc?.example && Array.isArray(desc?.example)) { - return ( -
- {/* Render description if it exists */} - {desc?.description && ( -

- {desc.description} -

- )} - {/* Render examples */} - {desc.example.map( - (exampleItem: any, exampleIndex: number) => { - const isExampleHeading = - exampleItem?.example_input?.startsWith( - 'Example' - ); - const isInputOrOutput = - exampleItem?.example_input === 'Input' || - exampleItem?.example_input === 'Output'; - const isStringOutput = - typeof exampleItem?.example_output === - 'string'; - const isArrayOutput = Array.isArray( - exampleItem?.example_output - ); - - return ( -
0 ? 'mt-3' : ''} - > - {isExampleHeading && ( - <> -

- {exampleItem.example_input} -

- {isStringOutput && ( -

- {exampleItem.example_output} -

- )} - - )} - {isInputOrOutput && ( - <> -

- {exampleItem.example_input} -

- {isArrayOutput && ( -
- {exampleItem.example_output.map( - ( - outputItem: any, - outputIndex: number - ) => ( -

- {outputItem?.value} -

- ) - )} -
- )} - - )} -
- ); - } - )} -
- ); - } - - // Handle regular description text - const descriptions = desc?.description || ""; - const splitDescriptions = descriptions.split(/((['"]).*?\2)/); // Split quoted and unquoted text - - return ( -

- {splitDescriptions.map( - (text: any, subIndex: any) => { - const isQuoted = - typeof text === "string" && /^(['"]).*\1$/.test(text); - const containsBetterBugs = - text.includes('BetterBugs.io'); - - if (containsBetterBugs) { - // Split the text around 'BetterBugs.io' to wrap it in a link - const parts = text.split('BetterBugs.io'); - return ( - - - {parts[0]} - - - BetterBugs.io - - - {parts[1]} - - - ); - } - - return ( - - {text} - - ); - } - )} -

- ); - } - )} - - {development_tools_about_details?.placeholder && ( -
    - {development_tools_about_details?.placeholder?.map( - (placeholder: any, index: any) => ( -
  • - - {placeholder?.title} - -
  • - ) - )} -
- )} -
- - {/* user agent info section */} - {development_tools_user_agent_info && ( -
- {development_tools_user_agent_info?.info_title && ( -

- {development_tools_user_agent_info?.info_title} -

- )} - {development_tools_user_agent_info?.intro_text && ( -

- {development_tools_user_agent_info?.intro_text} -

- )} - {development_tools_user_agent_info?.example_string && ( -

- {development_tools_user_agent_info?.example_string} -

- )} - {development_tools_user_agent_info?.example_string_description && ( -

- {development_tools_user_agent_info?.example_string_description} -

- )} - {development_tools_user_agent_info?.info_items && ( -
-
    - {development_tools_user_agent_info?.info_items?.map( - (item: any, index: number) => ( -
  • - - {item?.part}{' '} - - - {item?.description} - -
  • - ) - )} -
-
- )} -
- )} - - {/* step-by-step guide */} -
- {development_tools_steps_guide?.guide_title && ( - <> -
- {development_tools_steps_guide?.guide_title} -
-

- {development_tools_steps_guide?.guide_description - ?.split(/((['"]).*?\2)/g) - ?.map((parts: any, i: any) => { - const isQuoted = - typeof parts === "string" && - /^(['"]).*\1$/.test(parts); - return isQuoted ? ( - - {parts} - - ) : ( - - {parts} - - ); - })} -

- - )} - {development_tools_steps_guide?.steps?.length > 0 && ( -
- {development_tools_steps_guide?.steps?.map( - (guide: any, index: any) => { - const description = guide?.step_description; - const description2 = guide?.step_description2; - - // Split quoted and unquoted text - const parts = description?.split(/((['"]).*?\2)/); - const desParts = description2?.split(/((['"]).*?\2)/); - - return ( -
- {/* Primary Flex Layout for Step Key, Title, and Description */} -
- - {guide?.step_key} - - - {guide?.step_title} - -
- - {/* Step descriptions (below title) */} - {description && ( -
- {parts?.map((part: any, i: any) => { - const isQuoted = - typeof part === "string" && - /^(['"]).*\1$/.test(part); - return isQuoted ? ( - - {part} - - ) : ( - - {part} - - ); - })} -
- )} - - {/* Step Description2 on a New Line, Only If Present */} - {description2 && desParts?.length > 0 && ( -
- {desParts?.map((part: any, i: any) => { - const isQuoted = - typeof part === "string" && - /^(['"]).*\1$/.test(part); - return isQuoted ? ( - - {part} - - ) : ( - - {part} - - ); - })} -
- )} - - {guide?.steps_points?.length > 0 && ( -
    - {guide?.steps_points?.map((p: any, index: number) => ( -
  • - {p?.steps_points_title && ( - - {p?.steps_points_title} - - )} - {p?.steps_points_description && ( -

    - {p?.steps_points_description - ?.split(/((['"]).*?\2)/) - .map((part: string, i: number) => - typeof part === "string" && - /^(['"]).*\1$/.test(part) ? ( - - {part} - - ) : ( - - {part - .split(/(\/\/.*?\/\/)/) - .map((sub: string, j: number) => - sub.startsWith("//") && sub.endsWith("//") ? ( - - {sub.slice(2, -2)} - - ) : ( - sub - ) - )} - - ) - )} -

    - )} - {Array.isArray(p?.steps_subpoint) && p?.steps_subpoint?.length > 0 && ( -
      - {p?.steps_subpoint?.map((sub_p: any, subIndex: number) => ( -
    • - {sub_p?.title && ( - - {sub_p?.title} - - )} - {sub_p?.description && ( - - {sub_p?.description - ?.split(/((['"]).*?\2)/) - .map((part: string, i: number) => - typeof part === "string" && - /^(['"]).*\1$/.test(part) ? ( - - {part} - - ) : ( - - {part - .split(/(\/\/.*?\/\/)/) - .map((sub: string, j: number) => - sub.startsWith("//") && sub.endsWith("//") ? ( - - {sub.slice(2, -2)} - - ) : ( - sub - ) - )} - - ) - )} - - )} -
    • - ))} -
    - )} -
  • - ))} -
- )} -
- ); - } - )} -
- )} -
- - {/* example section */} - {development_tool_example && ( -
- {development_tool_example?.example_title && ( -

- {development_tool_example?.example_title} -

- )} - {development_tool_example?.example_description && ( -

- {development_tool_example?.example_description} -

- )} - - {development_tool_example?.example_input && ( -
- {development_tool_example?.example_input?.title && ( -

- {development_tool_example?.example_input?.title} -

- )} - {development_tool_example?.example_input - ?.json_data && ( -
-                              
-                                {
-                                  development_tool_example?.example_input
-                                    ?.json_data
-                                }
-                              
-                            
- )} -
- )} - - {development_tool_example?.example_outputs && ( -
- {development_tool_example?.example_outputs?.intro && ( -

- {development_tool_example?.example_outputs?.intro} -

- )} - {development_tool_example?.example_outputs?.outputs?.map( - (output: any, index: number) => ( -
- {output?.mode && ( -

- {output?.mode} -

- )} - {output?.title && ( -

- {output?.title} -

- )} - {output?.content && ( -
-                                    
-                                      {output?.content}
-                                    
-                                  
- )} - {output?.note && ( -

- {output?.note} -

- )} -
- ) - )} -
- )} - - {/* JavaScript example section (optional) */} - {development_tool_example?.javascript_example && ( -
- {development_tool_example?.javascript_example - ?.title && ( -
- { - development_tool_example?.javascript_example - ?.title - } -
- )} - {development_tool_example?.javascript_example - ?.description && ( -

- { - development_tool_example?.javascript_example - ?.description - } -

- )} - - {development_tool_example?.javascript_example?.methods - ?.length > 0 && ( -
-
    - {development_tool_example?.javascript_example?.methods?.map( - (method: any, index: number) => ( -
  • - {method?.name && ( - - {method?.name}{' '} - - )} - {method?.description && ( - - {method?.description} - - )} -
  • - ) - )} -
-
- )} - - {development_tool_example?.javascript_example - ?.examples?.length > 0 && ( -
- {development_tool_example?.javascript_example?.examples?.map( - (example: any, index: number) => ( -
- {example?.title && ( -

- {example?.title} -

- )} - {example?.code && ( -
-                                        
-                                          {example?.code}
-                                        
-                                      
- )} -
- ) - )} -
- )} - - {development_tool_example?.javascript_example - ?.note && ( -

- { - development_tool_example?.javascript_example - ?.note - } -

- )} -
- )} -
- )} - - {/* what section */} - {development_tools_what && ( -
- {development_tools_what?.about_title && ( -

- {development_tools_what?.about_title} -

- )} - {development_tools_what?.what_description?.map( - (desc: any, index: number) => { - const descriptions = desc?.descriptions; - const splitDescriptions = - descriptions.split(/((['"]).*?\2)/); // Split quoted and unquoted text - - return ( -

- {splitDescriptions.map( - (text: any, subIndex: any) => { - const isQuoted = - typeof text === "string" && - /^(['"]).*\1$/.test(text); - - return ( - - {text} - - ); - } - )} -

- ); - } - )} -
- )} - - {/* how to use */} -
- {development_tools_how_use?.how_use_title && ( - <> -
- {development_tools_how_use?.how_use_title} -
-

- {development_tools_how_use?.how_use_description} -

- - )} - {development_tools_how_use?.point?.length > 0 && ( -
-
    - {development_tools_how_use?.point?.map( - (how: any, index: any) => - how?.heading ? ( -

    - {how?.heading} -

    - ) : ( -
  • - <> - - {how?.title} - - - {how?.description} - - -
  • - ) - )} -
-
- )} -
- - {/* Comparison section */} - {development_tools_Comparison && ( -
- {development_tools_Comparison?.title && ( -

- {development_tools_Comparison?.title} -

- )} - {Array.isArray( - development_tools_Comparison?.description - ) && - development_tools_Comparison?.description.map( - (d: any, index: any) => ( -

- {d?.desc} -

- ) - )} -
- )} -
-
-
- - {/* cta section */} -
-
-
- {/* Left Section */} -
- BetterBugs Logo -
-

- The essential AI{' '} - companion every QA needs -

- - - -
- -

- Swiftly document and share bugs like never before -

-
- - - - - - -
-

- Free Forever. No Credit Card Required. -

-
- - {/* Right Section */} -
-
- - {/* Features Section */} -
-
- -

- Use AI{' '} - to reproduce and fix bugs -

-
-
- -

- - Screen Recording - {' '} - with{' '} - - Rewind (Upto 2-min) - -

-
-
- -

- Capture{' '} - - {' '} - backend logs - - with every bug report -

-
-
- -

- Assign bugs and{' '} - share at - single click -

-
-
- -
-
-
- - {/* Integrations Section */} -
-

- Integrations -

-

- Two-way sync with -
popular tools -

-
- {integrationTools?.map((tool, index) => ( - -
- - {tool?.icon} - -

- {tool?.name} -

-
- - ))} -
-
-
-
-
-
-
-
- - ); +const Page = async ({ params }: { params: Promise<{ slug: string }> }) => { + const { slug } = await params; + return ; }; export default Page; diff --git a/app/api/development-tools-category-content/route.ts b/app/api/development-tools-category-content/route.ts index b23dd61..604ce0a 100644 --- a/app/api/development-tools-category-content/route.ts +++ b/app/api/development-tools-category-content/route.ts @@ -1,6 +1,8 @@ import { NextResponse } from 'next/server'; import { developmentToolsCategoryContent } from '@/app/libs/constants'; +export const dynamic = 'force-static'; + export async function GET() { console.log( 'developmentToolsCategoryContent', diff --git a/app/components/collapseFAQComponent/collapseFAQComponent.tsx b/app/components/collapseFAQComponent/collapseFAQComponent.tsx index 7f16a54..3c4026e 100644 --- a/app/components/collapseFAQComponent/collapseFAQComponent.tsx +++ b/app/components/collapseFAQComponent/collapseFAQComponent.tsx @@ -9,24 +9,26 @@ export interface CollapseFAQComponentProps { const CollapseFAQComponent = (props: CollapseFAQComponentProps) => { const { FAQs } = props; - const { Panel } = Collapse; return (
- - {FAQs?.map((faq: any) => ( - - {faq?.title} -
- } - key={faq?.key} - > + ({ + key: faq?.key, + label: ( +
+ {faq?.title} +
+ ), + children: (

{faq?.des}

- - ))} -
+ ), + }))} + /> ); }; diff --git a/app/components/comparisonsComponent/comparisonsStyles.module.scss b/app/components/comparisonsComponent/comparisonsStyles.module.scss index 7aa1652..4a0f77e 100644 --- a/app/components/comparisonsComponent/comparisonsStyles.module.scss +++ b/app/components/comparisonsComponent/comparisonsStyles.module.scss @@ -9,16 +9,6 @@ height: 100%; } -// .testimonialCard { -// margin-top: 40px; -// background: linear-gradient(180deg, #000000 0%, rgba(102, 102, 102, 0) 100%); -// border-top: 1px solid #00da92; -// border-right: 1px solid #00da92; -// border-left: 1px solid #00da92; -// border-bottom: 0; -// border-radius: 20px; -// } - .ctaCardGridBg { background: linear-gradient( 180deg, @@ -34,34 +24,32 @@ inset 0 1px 0 rgba(255, 255, 255, 0.9); } -.collapse { - :global(.ant-collapse) { - background-color: transparent !important; - - :global(.ant-collapse-expand-icon) { - color: white !important; - order: 2; - } +.collapse :global(.ant-collapse) { + background-color: transparent !important; +} - @media (min-width: 1024px) { - :global(.ant-collapse-header) { - padding: 0px 0px 16px 0px !important; - } - } +.collapse :global(.ant-collapse .ant-collapse-expand-icon) { + color: white !important; + order: 2; +} - @media (max-width: 564px) { - :global(.ant-collapse-header) { - padding: 16px 0px 16px 0px !important; - align-items: center !important; - } - } +@media (min-width: 1024px) { + .collapse :global(.ant-collapse .ant-collapse-header) { + padding: 0px 0px 16px 0px !important; + } +} - :global(.ant-collapse-content-box) { - padding: 0px 0px 20px 0px !important; - } +@media (max-width: 564px) { + .collapse :global(.ant-collapse .ant-collapse-header) { + padding: 16px 0px 16px 0px !important; + align-items: center !important; } } +.collapse :global(.ant-collapse .ant-collapse-content-box) { + padding: 0px 0px 20px 0px !important; +} + .borderBottom { &:last-child { border-bottom: 5px solid #00da92; diff --git a/app/components/developmentToolsComponent/epochConverter.tsx b/app/components/developmentToolsComponent/epochConverter.tsx index dfc9017..af8d025 100644 --- a/app/components/developmentToolsComponent/epochConverter.tsx +++ b/app/components/developmentToolsComponent/epochConverter.tsx @@ -97,7 +97,7 @@ const EpochConverter = () => {
{ const [inputCode, setInputCode] = useState(""); const [outputCode, setOutputCode] = useState(""); - const handleObfuscate = () => { + const handleObfuscate = async () => { if (!inputCode.trim()) { toast.error("Error: Cannot obfuscate an empty file!"); - return; // Don't proceed with obfuscation if the code is empty + return; } try { + const { default: JavaScriptObfuscator } = await import("javascript-obfuscator"); const obfuscatedCode = JavaScriptObfuscator.obfuscate(inputCode, { compact: true, controlFlowFlattening: true, }).getObfuscatedCode(); setOutputCode(obfuscatedCode); } catch (error) { - setOutputCode(""); // Clear the output on error - toast.error(`Error obfuscating code: ${(error as Error).message}`); // Set the obfuscation error message + setOutputCode(""); + toast.error(`Error obfuscating code: ${(error as Error).message}`); } }; diff --git a/app/components/developmentToolsComponent/randomDateGenerator.tsx b/app/components/developmentToolsComponent/randomDateGenerator.tsx index 6438dac..bdc8ef8 100644 --- a/app/components/developmentToolsComponent/randomDateGenerator.tsx +++ b/app/components/developmentToolsComponent/randomDateGenerator.tsx @@ -22,7 +22,7 @@ const RandomDateGenerator = () => { const startInputRef = useRef(null); const endInputRef = useRef(null); - const openPicker = (ref: React.RefObject) => { + const openPicker = (ref: React.RefObject) => { const el = ref.current; if (!el) return; // @ts-ignore - showPicker is supported in modern Chromium diff --git a/app/components/headerMenuDropdownComponents/productMenuComponent.tsx b/app/components/headerMenuDropdownComponents/productMenuComponent.tsx index 9ea84f4..83603ff 100644 --- a/app/components/headerMenuDropdownComponents/productMenuComponent.tsx +++ b/app/components/headerMenuDropdownComponents/productMenuComponent.tsx @@ -1,3 +1,4 @@ +'use client'; import Image from 'next/image'; import Link from 'next/link'; import { usePathname } from 'next/navigation'; diff --git a/app/components/headerMenuDropdownComponents/resourceMenuComponent.tsx b/app/components/headerMenuDropdownComponents/resourceMenuComponent.tsx index 0e92645..8e42b89 100644 --- a/app/components/headerMenuDropdownComponents/resourceMenuComponent.tsx +++ b/app/components/headerMenuDropdownComponents/resourceMenuComponent.tsx @@ -1,3 +1,4 @@ +'use client'; import React from 'react'; import { usePathname } from 'next/navigation'; import Link from 'next/link'; diff --git a/app/components/layout/Header/headerComponent.tsx b/app/components/layout/Header/headerComponent.tsx index 544d22a..6ea72cb 100644 --- a/app/components/layout/Header/headerComponent.tsx +++ b/app/components/layout/Header/headerComponent.tsx @@ -1,5 +1,5 @@ 'use client'; -import { Button, Collapse, Drawer, Dropdown, Menu } from 'antd'; +import { Button, Collapse, Drawer, Dropdown } from 'antd'; import Image from 'next/image'; import Link from 'next/link'; import { usePathname, useSearchParams } from 'next/navigation'; @@ -386,12 +386,7 @@ const HeaderComponent = () => { (menu.label === 'Resources' && menu?.dropdown) ? ( ({ - key: index, - label:

{item?.label}

, - })), - }} + popupRender={() => menu?.dropdown?.[0]?.label ?? null} trigger={['hover']} >

{

- - {responsiveContentHeader?.map((item: any, index) => ( - - {item.header} -

- } - key={index} - > - {item.content &&

{item.content}

} -
- ))} -
+ ({ + key: index, + className: hederStyles.accordion, + label: ( +

+ {item.header} +

+ ), + children: item.content &&

{item.content}

, + }))} + /> {responsiveHeader?.map((data: any, index: any) => (

{ + if (!pathname || pathname === "/") return CANONICAL_BASE_PATH; + + if ( + pathname === CANONICAL_BASE_PATH || + pathname.startsWith(`${CANONICAL_BASE_PATH}/`) + ) { + return pathname; + } + + return `${CANONICAL_BASE_PATH}${pathname.startsWith("/") ? pathname : `/${pathname}`}`; +}; + const CanonicalLink = () => { const router = usePathname(); - let url = ""; - if (router === "/use-cases/[slug]") { - url = WEB_URL + router; - } else { - url = WEB_URL + router; - } - const canonicalURL = url; + const canonicalURL = WEB_URL + buildCanonicalPath(router); return ( <> diff --git a/app/layout.tsx b/app/layout.tsx index 00e570a..76b2287 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -1,38 +1,16 @@ -'use client'; import 'antd/dist/reset.css'; import './styles/global.scss'; import { Poppins } from 'next/font/google'; -import HeaderComponent from './components/layout/Header/headerComponent'; -import CanonicalLink from './components/theme/canonicalLink/canonicalLink'; -import { - LayoutContext, - LayoutContextModel, - LayoutContextProvider, -} from './contexts/layoutContexts'; -import { ThemeProvider } from './contexts/themeContext'; -import FooterComponent from './components/layout/footer/footerComponent'; -import { useMediaQuery } from 'react-responsive'; -import { Suspense, useContext } from 'react'; -import { CookiesProvider } from 'react-cookie'; +import { AntdRegistry } from '@ant-design/nextjs-registry'; import Script from 'next/script'; -import dynamic from 'next/dynamic'; - -const AnimatedCursor = dynamic(() => import('react-animated-cursor'), { - ssr: false, -}); +import ClientLayout from './ClientLayout'; const inter = Poppins({ subsets: ['latin'], weight: ['100', '200', '300', '400', '500', '600', '700', '800', '900'], }); -const MyApp = ({ children }: { children: JSX.Element }): JSX.Element => { - const { isClient }: LayoutContextModel = useContext(LayoutContext); - - const isDesktopOrLaptop = useMediaQuery({ - query: '(min-width: 1024px)', - }); - +const RootLayout = ({ children }: { children: React.ReactNode }) => { return ( @@ -41,7 +19,7 @@ const MyApp = ({ children }: { children: JSX.Element }): JSX.Element => { content="MdnRMoBETw7d7ltRJoXu8nAIg6ah5iUdVXpNp_fFGH8" /> - + + /> - + /> +