Skip to content
This repository was archived by the owner on Apr 25, 2023. It is now read-only.

Added Competitor Pages #441

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 84 additions & 0 deletions components/Competitors/ComparisonTable.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { QuestionMarkCircleIcon } from '@heroicons/react/20/solid'
import Image from 'next/image'
import { HiCheckCircle, HiDotsCircleHorizontal, HiXCircle } from 'react-icons/hi'
import highlightlogosmall from '../../public/images/logo-on-dark.png'
import logrocketlogofull from '../../public/images/logrocketlogofull.png'
import logrocketlogosmall from '../../public/images/logrocketlogosmall.png'
import { HighlightLogo } from '../common/HighlightLogo/HighlightLogo'
import { Typography } from '../common/Typography/Typography'
import { ComparisonTableRow, ComparisonTableSection, Competitor } from './competitors'

export default function ComparisonTable(props: { competitor: Competitor }) {
if (!props.competitor) {
return <div>No Competitor Loaded</div>
}

return (
<div className="">
{props.competitor.sections.map((section: ComparisonTableSection, i) => (
<div key={`${section.title}` + `${i}`} className="w-full mb-8">
<div className="flex justify-between mb-2 items-end">
<Typography type="copy1" className="text-white" emphasis>
{section.title}
</Typography>
<div className={`${i == 0 ? 'flex' : 'hidden '} items-center`}>
<div className="w-[50px] md:w-[200px] px-2">
<div className="hidden md:flex">
<HighlightLogo />
</div>
<Image src={highlightlogosmall} alt="LogRocket Logo" className="md:hidden" />
</div>
<div className="w-[50px] md:w-[200px] px-1">
<Image src={logrocketlogofull} alt="LogRocket Logo" className="hidden md:flex" />
<Image src={logrocketlogosmall} alt="LogRocket Logo" className="md:hidden" />
</div>
</div>
</div>
{section.rows.map((row: ComparisonTableRow, j) => (
<div key={`${row.feature}` + `${j}`} className="flex w-full justify-between items-center">
<Typography type="copy2" className="text-copy-on-dark">
{row.feature}
</Typography>
<div className="flex items-center">
<QuestionMarkCircleIcon className="h-5 w-5 mr-2 text-copy-on-light " />

<div
className={`bg-divider-on-dark px-2 py-2 w-[50px] md:w-[200px] border-r-[1px] border-copy-on-light ${
j == 0 ? 'rounded-tl-lg' : j == section.rows.length - 1 ? 'rounded-bl-lg' : ''
}`}
>
{row.highlight == 1 ? (
<HiCheckCircle className="text-copy-on-dark h-7 w-7" />
) : (
<div className="flex gap-1">
<HiXCircle className="hidden md:flex text-copy-on-light h-7 w-7" />
<HiDotsCircleHorizontal className="md:hidden text-copy-on-light h-7 w-7" />
{row.highlight == 0.5 && (
<div className="hidden md:flex items-center bg-copy-on-light px-3 rounded-full">
<Typography type="copy4" emphasis className="text-copy-on-dark rounded-full">
Coming soon
</Typography>
</div>
)}
</div>
)}
</div>
<div
className={`bg-divider-on-dark px-2 py-2 w-[50px] md:w-[200px] ${
j == 0 ? 'rounded-tr-lg' : j == section.rows.length - 1 ? 'rounded-br-lg' : ''
}`}
>
{row.competitor ? (
<HiCheckCircle className="text-copy-on-dark h-7 w-7" />
) : (
<HiXCircle className="text-copy-on-light h-7 w-7" />
)}
</div>
</div>
</div>
))}
</div>
))}
</div>
)
}
120 changes: 120 additions & 0 deletions components/Competitors/competitors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
import { StaticImageData } from 'next/image'

export type ComparisonTableRow = {
feature: string
highlight: 0 | 0.5 | 1
competitor: 0 | 1
tooltip?: string
}

export type ComparisonTableSection = {
title: string
rows: ComparisonTableRow[]
}

export type Competitor = {
slug: string
name: string
header: string
subheader: string
subHeader2: string
logo?: StaticImageData
sections: ComparisonTableSection[]
}

export const COMPETITORS: { [k: string]: Competitor } = {
'log-rocket': {
slug: 'log-rocket',
name: 'LogRocket',
header: 'The Open Source Logrocket Alternative.',
subheader: 'Pixel-perfect video replay of your web application. Step into the shoes of your users.',
subHeader2: 'An Open Source, Fullstack Alternative to Logrocket. Get Started for free in minutes.',
sections: [
{
title: 'General',
rows: [
{
feature: 'Unlimited Team Members',
highlight: 1,
competitor: 1,
},
{
feature: 'Unlimited Team Members',
highlight: 1,
competitor: 0,
},
{
feature: 'Unlimited Team Members',
highlight: 1,
competitor: 1,
},
{
feature: 'Unlimited Team Members',
highlight: 0.5,
competitor: 1,
},
{
feature: 'Unlimited Team Members',
highlight: 1,
competitor: 1,
},
],
},
{
title: 'Session Replay',
rows: [
{
feature: 'Commenting / Mentions',
highlight: 1,
competitor: 1,
},
{
feature: 'Heatmaps',
highlight: 1,
competitor: 0,
},
{
feature: 'Session Sharing',
highlight: 1,
competitor: 1,
},
{
feature: 'Privacy SDKs',
highlight: 0.5,
competitor: 0,
},
{
feature: 'Embedded Error Monitoring',
highlight: 1,
competitor: 1,
},
{
feature: 'Canvas & WebGL Recording',
highlight: 1,
competitor: 1,
},
{
feature: 'Session-level Debugging Data',
highlight: 1,
competitor: 1,
},
],
},
{
title: 'Error Monitoring',
rows: [
{
feature: 'Embedded Session Replay',
highlight: 1,
competitor: 1,
},
{
feature: 'Support for Backend SDKs',
highlight: 1,
competitor: 0,
},
],
},
],
},
}
162 changes: 162 additions & 0 deletions pages/session-replay/[slug].tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
import classNames from 'classnames'
import { GetStaticPaths, GetStaticProps } from 'next'
import Image from 'next/image'
import Link from 'next/link'
import { useState } from 'react'
import { MdKeyboardReturn } from 'react-icons/md'
import { PrimaryButton } from '../../components/common/Buttons/PrimaryButton'
import { FooterCallToAction } from '../../components/common/CallToAction/FooterCallToAction'
import { OSSCallToAction } from '../../components/common/CallToAction/OSSCallToAction'
import Footer from '../../components/common/Footer/Footer'
import Navbar from '../../components/common/Navbar/Navbar'
import { Section } from '../../components/common/Section/Section'
import { Typography } from '../../components/common/Typography/Typography'
import CompetitorTable from '../../components/Competitors/ComparisonTable'
import { Competitor, COMPETITORS } from '../../components/Competitors/competitors'
import { CompaniesReel } from '../../components/Home/CompaniesReel/CompaniesReel'
import { CustomerReviewTrack } from '../../components/Home/CustomerReviewTrack'
import homeStyles from '../../components/Home/Home.module.scss'
import errorMonitoringHero from '../../public/images/features/errorMonitoringHero.png'
import loggingHero from '../../public/images/features/loggingHero.png'
import sessionReplayHero from '../../public/images/features/sessionReplayHero.png'
import sessionscreenshot from '../../public/images/sessionscreenshot.png'

import { AnimateFeatureHeroRight, AnimateFeatureHeroXL } from '../../components/Animate'

const CompetitorComparisonPage = ({ competitor }: { competitor: Competitor }) => {
const [imageLoaded, setImageLoaded] = useState(false)

return (
<div>
<Image src={sessionReplayHero} alt="Hero Background" className="hidden" />
<Image src={errorMonitoringHero} alt="Hero Background" className="hidden" />
<Image src={loggingHero} alt="Hero Background" className="hidden" />
<Navbar />
<div className="hidden md:flex ml-10 my-2">
<Link href="/">
<Typography type="copy3" emphasis={true}>
<div className="flex items-center justify-start gap-2">
<MdKeyboardReturn className="h-5" /> Explore highlight.io
</div>
</Typography>
</Link>
</div>
<main>
<div className="flex flex-col xl:flex-row justify-between w-screen px-8 mx-auto lg:px-4 lg:py-28 max-w-[1200px] 2xl:max-w-[1400px]">
<div className="lg:w-[60%] flex justify-center mt-10">
<div className="flex flex-col max-w-4xl gap-8 text-center lg:text-left">
<h2 className="text-white">The Open Source {competitor.name} alternative.</h2>

<Typography type="copy1" className="lg:w-3/4 text-copy-on-dark">
{competitor.subheader}
</Typography>
<div className="flex flex-col lg:flex-row justify-start gap-4 w-full lg:w-auto">
<PrimaryButton
className={classNames(homeStyles.solidButton, 'min-w-[180px]')}
href="https://app.highlight.io/?sign_up=1"
>
<Typography type="copy2" emphasis={true}>
Get started for free
</Typography>
</PrimaryButton>
<PrimaryButton
href={'/docs/general/product-features/session-replay/overview'}
className={classNames(homeStyles.hollowButton)}
>
<Typography type="copy2" emphasis={true}>
Read our docs
</Typography>
</PrimaryButton>
</div>
</div>
</div>
<div className="flex justify-center mt-12 lg:mt-0 ultra:relative">
<AnimateFeatureHeroRight loaded={imageLoaded}>
<Image
className={`hidden lg:flex ultra:hidden right-0 object-contain top-0`}
src={sessionReplayHero}
alt="Feature Spotlight"
onLoadingComplete={() => setImageLoaded(true)}
/>
</AnimateFeatureHeroRight>
<AnimateFeatureHeroXL loaded={imageLoaded}>
<Image
className={`hidden ultra:flex`}
src={sessionscreenshot}
alt="Feature Spotlight"
onLoadingComplete={() => setImageLoaded(true)}
/>
</AnimateFeatureHeroXL>
<Image
className={`lg:hidden right-0 object-contain bottom-0 md:w-[500px]`}
src={sessionscreenshot}
alt="Feature Spotlight"
onLoadingComplete={() => setImageLoaded(true)}
/>
</div>
</div>
<div className="w-full mx-auto max-w-screen-2xl mt-24 lg:mt-36">
<Section className="flex flex-col gap-20">
<div className="mx-auto max-w-[1100px]">
<h2 className="self-center text-center">
How does <span className="text-color-selected-light">highlight.io</span> stack up?
</h2>
<div className="px-8 max-w-[1000px] mx-auto mt-6 text-center">
<Typography type="copy2" className="text-copy-on-dark text-center">
{competitor.subHeader2}
</Typography>
</div>
</div>
<CompetitorTable competitor={competitor} />
</Section>
</div>

<OSSCallToAction />
<Section>
<CompaniesReel />
</Section>
<Section>
<div className={homeStyles.anchorFeature}>
<div className={homeStyles.anchorHead}>
<Typography type="copy2" onDark>
Don&apos;t take our word. <Link href="/customers">Read our customer review section →</Link>
</Typography>
</div>
</div>
</Section>
<CustomerReviewTrack />
<FooterCallToAction />
</main>
<Footer />
</div>
)
}

export const getStaticPaths: GetStaticPaths = async () => {
return {
paths: Object.keys(COMPETITORS).map((k: string) => ({
params: { slug: k },
})),
fallback: 'blocking',
}
}

//Gets list of products from products.ts
export const getStaticProps: GetStaticProps = async ({ params }) => {
const slug = params?.slug as string

// Handle event slugs which don't exist
if (!COMPETITORS[slug]) {
return {
notFound: true,
}
}

return {
props: {
competitor: COMPETITORS[slug],
},
}
}

export default CompetitorComparisonPage
Binary file added public/images/logrocketlogofull.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/images/logrocketlogosmall.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.