mirror of
https://github.com/dathere/qsvpro.dathere.com.git
synced 2025-12-19 05:59:24 +00:00
feat: initial commit!
This commit is contained in:
commit
3c04acba10
53 changed files with 21596 additions and 0 deletions
57
src/components/Brands.jsx
Normal file
57
src/components/Brands.jsx
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
import { motion } from "framer-motion";
|
||||
|
||||
import { AmazonLogo } from "../assets/logos/AmazonLogo";
|
||||
import { DropboxLogo } from "../assets/logos/DropboxLogo";
|
||||
import { NetflixLogo } from "../assets/logos/NetflixLogo";
|
||||
import { SlackLogo } from "../assets/logos/SlackLogo";
|
||||
import { SpotifyLogo } from "../assets/logos/SpotifyLogo";
|
||||
import { StripeLogo } from "../assets/logos/StripeLogo";
|
||||
|
||||
export const Brands = () => (
|
||||
<section className="py-12 sm:py-24 bg-customDarkBg1 w-full mt-16 mb-16">
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
whileInView={{ opacity: 1 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.5, delay: 0.2 }}
|
||||
>
|
||||
<div className="container px-4 mx-auto 2xl:w-[1200px] xl:w-[1100px] lg:w-[1000px] md:w-4/5">
|
||||
<div className="flex lg:flex-row flex-col items-center -mx-4 justify-center lg:text-left text-center">
|
||||
<div className="w-full lg:w-1/2 px-4 mb-12 lg:mb-0">
|
||||
<div className="flex flex-col">
|
||||
<h2 className="mb-2 text-4xl sm:text-5xl 2xl:text-6xl font-bold tracking-normal text-white">
|
||||
Trusted by brands
|
||||
</h2>
|
||||
<h2 className=" text-4xl sm:text-5xl 2xl:text-6xl font-bold tracking-normal text-customSecondary">
|
||||
all over the world
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
<div className="w-2/3 sm:w-[620px] lg:w-1/2 mx-auto lg:mx-0 lg:pl-10">
|
||||
<div className="flex flex-wrap -m-4">
|
||||
<div className="w-1/2 sm:w-1/3 py-6 flex justify-center">
|
||||
<AmazonLogo />
|
||||
</div>
|
||||
<div className="w-1/2 sm:w-1/3 py-6 flex justify-center">
|
||||
<DropboxLogo />
|
||||
</div>
|
||||
<div className="w-1/2 sm:w-1/3 py-6 flex justify-center">
|
||||
<NetflixLogo />
|
||||
</div>
|
||||
<div className="w-1/2 sm:w-1/3 py-6 flex justify-center">
|
||||
<StripeLogo />
|
||||
</div>
|
||||
|
||||
<div className="w-1/2 sm:w-1/3 py-6 flex justify-center">
|
||||
<SpotifyLogo />
|
||||
</div>
|
||||
<div className="w-1/2 sm:w-1/3 py-6 flex justify-center">
|
||||
<SlackLogo />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
</section>
|
||||
);
|
||||
5
src/components/Divider.jsx
Normal file
5
src/components/Divider.jsx
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
export const Divider = () => (
|
||||
<div className="w-full lg:w-3/5 mx-auto">
|
||||
<div className="border-t border-customGrayBorder"></div>
|
||||
</div>
|
||||
);
|
||||
96
src/components/FAQ.jsx
Normal file
96
src/components/FAQ.jsx
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
import { useState } from "react";
|
||||
import { motion } from "framer-motion";
|
||||
|
||||
const FAQData = [
|
||||
{
|
||||
question: "What is qsv pro?",
|
||||
answer: "qsv pro is a desktop application that allows you to run qsv commands with a graphical user interface based on the qsv CLI tool. It also features a suite of recipes (scripts) for common data wrangling tasks to perform on your spreadsheet, including sorting rows, removing duplicate rows, and removing Personally Identifiable Information (PII).",
|
||||
},
|
||||
{
|
||||
question: "How do I get qsv pro?",
|
||||
answer: "qsv pro is currently in beta. You can get beta access by purchasing a subscription to qsv pro on our store. You will receive an email with a download link to qsv pro after purchasing a subscription, or you may click the Download button on this page's navigation bar to download qsv pro (license key still required).",
|
||||
},
|
||||
{
|
||||
question: "Where can I provide feedback?",
|
||||
answer: "There is a feedback button in the top right corner of qsv pro. You may also email us at dathere.com/contact.",
|
||||
},
|
||||
];
|
||||
|
||||
export const FAQ = () => (
|
||||
<section className="relative pt-16 pb-16 bg-blueGray-50 overflow-hidden">
|
||||
<div className="absolute -top-10" id="FAQ" />
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
whileInView={{ opacity: 1 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.5, delay: 0.2 }}
|
||||
>
|
||||
<div className="relative z-10 container px-2 sm:px-8 lg:px-4 mx-auto w-11/12 sm:w-full">
|
||||
<div className="md:max-w-4xl mx-auto">
|
||||
<p className="mb-7 custom-block-subtitle text-center">
|
||||
Have any questions?
|
||||
</p>
|
||||
<h2 className="mb-16 custom-block-big-title text-center">
|
||||
Frequently Asked Questions
|
||||
</h2>
|
||||
<div className="mb-11 flex flex-wrap -m-1">
|
||||
{FAQData.map((item, index) => (
|
||||
<div className="w-full p-1">
|
||||
<FAQBox
|
||||
title={item.question}
|
||||
content={item.answer}
|
||||
key={`${item.question}-${item.answer}`}
|
||||
defaultOpen={index === 0}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
</section>
|
||||
);
|
||||
|
||||
const FAQBox = ({ defaultOpen, title, content }) => {
|
||||
const [isOpen, setIsOpen] = useState(defaultOpen);
|
||||
|
||||
return (
|
||||
<div
|
||||
className="pt-2 sm:pt-6 pb-2 px-3 sm:px-8 rounded-3xl bg-customDarkBg3 custom-border-gray-darker mb-4 relative hover:bg-customDarkBg3Hover cursor-pointer"
|
||||
onClick={() => setIsOpen(!isOpen)}
|
||||
>
|
||||
<div className="flex flex-col p-2 justify-center items-start">
|
||||
<h3 className=" custom-content-title pt-3 sm:pt-0 pr-8 sm:pr-0">
|
||||
{title}
|
||||
</h3>
|
||||
<p
|
||||
className={`text-customGrayText pt-4 transition-all duration-300 overflow-hidden ${
|
||||
isOpen ? "max-h-96" : "max-h-0"
|
||||
}`}
|
||||
>
|
||||
{content}
|
||||
</p>
|
||||
</div>
|
||||
<div className="absolute top-6 right-4 sm:top-8 sm:right-8">
|
||||
<svg
|
||||
width="28px"
|
||||
height="30px"
|
||||
viewBox="0 0 20 20"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className={`transition-all duration-500 ${
|
||||
isOpen ? "rotate-[180deg]" : "rotate-[270deg]"
|
||||
}`}
|
||||
>
|
||||
<path
|
||||
d="M4.16732 12.5L10.0007 6.66667L15.834 12.5"
|
||||
stroke="#007AFF"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
></path>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
75
src/components/Features0.jsx
Normal file
75
src/components/Features0.jsx
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
import { motion } from "framer-motion";
|
||||
|
||||
import draganddrop from "../assets/images/drag-and-drop.gif";
|
||||
import { CheckArrowIcon } from "../assets/icons/CheckArrowIcon";
|
||||
|
||||
export const Features0 = () => {
|
||||
return (
|
||||
<section
|
||||
className="w-full bg-customDarkBg2 mt-20 mb-8 sm:mt-16 sm:mb-16 xl:mt-0 pt-[2rem] md:pt-[12vw] lg:pt-16"
|
||||
id="features"
|
||||
>
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
whileInView={{ opacity: 1 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.5, delay: 0.2 }}
|
||||
>
|
||||
<div className="flex flex-wrap items-center 2xl:w-[1450px] xl:w-[1300px] w-11/12 mx-auto md:pl-4 xl:pr-16 xl:pl-16">
|
||||
<div className="w-full md:w-1/2 mb-8 md:mb-0 lg:pl-16 flex justify-center mx-auto pt-16 lg:pt-0">
|
||||
<img
|
||||
src={draganddrop}
|
||||
alt="Drag and Drop Demo"
|
||||
className="rounded-xl custom-border-gray"
|
||||
/>
|
||||
</div>
|
||||
<div className="w-full lg:w-1/2 mb-12 lg:mb-0">
|
||||
<div className="mx-auto lg:mx-auto w-11/12 sm:w-4/5 md:w-3/4 lg:w-unset">
|
||||
<span className="custom-block-subtitle">
|
||||
Import Your Data
|
||||
</span>
|
||||
<h2 className="mt-6 mb-8 text-4xl lg:text-5xl custom-block-big-title">
|
||||
<u>Drag & drop</u> your data into qsv pro
|
||||
</h2>
|
||||
<p className="mb-10 text-customGrayText leading-loose">
|
||||
Drag and drop a spreadsheet file anywhere within
|
||||
the Workflow section to import your data and
|
||||
begin exploring your data in a data table.
|
||||
</p>
|
||||
<ul className="mb-6 text-white">
|
||||
<li className="mb-4 flex">
|
||||
<CheckArrowIcon />
|
||||
<span>
|
||||
File formats: .csv, .tsv, .tab, .xlsx,
|
||||
.xls, .ods, .xlsm, .xlsb
|
||||
</span>
|
||||
</li>
|
||||
<li className="mb-4 flex">
|
||||
<CheckArrowIcon />
|
||||
<span>
|
||||
Supports large spreadsheets with
|
||||
millions of rows
|
||||
</span>
|
||||
</li>
|
||||
<li className="mb-4 flex">
|
||||
<CheckArrowIcon />
|
||||
<span>
|
||||
Begins scanning and analyzing your data
|
||||
on import
|
||||
</span>
|
||||
</li>
|
||||
<li className="mb-4 flex">
|
||||
<CheckArrowIcon />
|
||||
<span>
|
||||
Generates stats, frequency, and file
|
||||
metadata
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
59
src/components/Features1.jsx
Normal file
59
src/components/Features1.jsx
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
import { motion } from "framer-motion";
|
||||
|
||||
import recipedemo from "../assets/images/recipe-demo.gif";
|
||||
import { CheckArrowIcon } from "../assets/icons/CheckArrowIcon";
|
||||
|
||||
export const Features1 = () => {
|
||||
return (
|
||||
<section className="w-full bg-customDarkBg2 mt-20 mb-8 sm:mt-16 sm:mb-16 xl:mt-4 pt-16">
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
whileInView={{ opacity: 1 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.5, delay: 0.2 }}
|
||||
>
|
||||
<div className="flex flex-wrap items-center 2xl:w-[1450px] xl:w-[1300px] w-11/12 mx-auto md:pl-4 xl:pr-16 xl:pl-16">
|
||||
<div className="w-full lg:w-1/2 mb-12 lg:mb-0">
|
||||
<div className="mx-auto lg:mx-auto w-11/12 sm:w-4/5 md:w-3/4 lg:w-unset">
|
||||
<span className="custom-block-subtitle">
|
||||
Transform Your Data
|
||||
</span>
|
||||
<h2 className="mt-6 mb-8 text-4xl lg:text-5xl custom-block-big-title">
|
||||
Transform your data with <u>recipes</u>
|
||||
</h2>
|
||||
<p className="mb-10 text-customGrayText leading-loose">
|
||||
qsv pro features a suite of recipes (scripts)
|
||||
for common data wrangling tasks to perform on
|
||||
your spreadsheet, including:
|
||||
</p>
|
||||
<ul className="mb-6 text-white">
|
||||
<li className="mb-4 flex">
|
||||
<CheckArrowIcon />
|
||||
<span>Sort rows</span>
|
||||
</li>
|
||||
<li className="mb-4 flex">
|
||||
<CheckArrowIcon />
|
||||
<span>Remove duplicate rows</span>
|
||||
</li>
|
||||
<li className="mb-4 flex">
|
||||
<CheckArrowIcon />
|
||||
<span>
|
||||
Remove Personally Identifiable
|
||||
Information (PII)
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div className="w-full md:w-1/2 mb-8 md:mb-0 mx-auto lg:w-1/2 flex flex-wrap lg:-mx-4 sm:pr-8 justify-center">
|
||||
<img
|
||||
src={recipedemo}
|
||||
alt="Recipe Demo"
|
||||
className="rounded-xl custom-border-gray"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
55
src/components/Features2.jsx
Normal file
55
src/components/Features2.jsx
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
import { motion } from "framer-motion";
|
||||
|
||||
import uploadtockan from "../assets/images/upload-to-ckan.gif";
|
||||
import { CheckArrowIcon } from "../assets/icons/CheckArrowIcon";
|
||||
|
||||
export const Features2 = () => (
|
||||
<section className="w-full bg-customDarkBg2 mt-12 sm:mt-20 mb-10 lg:my-20 pt-4">
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
whileInView={{ opacity: 1 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.5, delay: 0.2 }}
|
||||
>
|
||||
<div className="flex flex-wrap items-center 2xl:w-[1450px] xl:w-[1300px] w-11/12 mx-auto md:pl-4 xl:pr-16 xl:pl-16">
|
||||
<div className="w-full md:w-1/2 mb-8 md:mb-0 lg:pl-16 flex justify-center mx-auto pt-16 lg:pt-0">
|
||||
<img
|
||||
src={uploadtockan}
|
||||
alt="CKAN Upload Demo"
|
||||
className="rounded-xl custom-border-gray"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="w-full lg:w-1/2 mb-12 lg:mb-0 xl:pl-8">
|
||||
<div className="mx-auto lg:mx-auto w-11/12 sm:w-4/5 md:w-3/4 lg:w-unset">
|
||||
<span className="custom-block-subtitle">
|
||||
Load to CKAN with ease
|
||||
</span>
|
||||
<h2 className="mt-6 mb-8 text-4xl lg:text-5xl custom-block-big-title">
|
||||
Upload your data to a <u>CKAN</u> datastore
|
||||
</h2>
|
||||
<p className="mb-12 text-customGrayText leading-loose">
|
||||
qsv pro allows you to upload your transformed data
|
||||
to a CKAN datastore & a data dictionary to specify
|
||||
file metadata.
|
||||
</p>
|
||||
<ul className="mb-6 text-white">
|
||||
<li className="mb-4 flex">
|
||||
<CheckArrowIcon />
|
||||
<span>Integrated with the CKAN API</span>
|
||||
</li>
|
||||
<li className="mb-4 flex">
|
||||
<CheckArrowIcon />
|
||||
<span>In-app data dictionary form</span>
|
||||
</li>
|
||||
<li className="mb-4 flex">
|
||||
<CheckArrowIcon />
|
||||
<span>Automatically inferred data types</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
</section>
|
||||
);
|
||||
76
src/components/FeaturesDiagonal.jsx
Normal file
76
src/components/FeaturesDiagonal.jsx
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
import { motion } from "framer-motion";
|
||||
import { useState } from "react";
|
||||
|
||||
import configurator from "../assets/images/configurator.gif";
|
||||
|
||||
export const FeaturesDiagonal = () => {
|
||||
return (
|
||||
<section className="lg:mb-16 w-full flex flex-col justify-center items-center bg-customDarkBg1">
|
||||
<div className="custom-shape-divider-bottom-1665696614">
|
||||
<svg
|
||||
data-name="Layer 1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 1200 120"
|
||||
preserveAspectRatio="none"
|
||||
className="custom-bg-dark2"
|
||||
>
|
||||
<path
|
||||
d="M1200 120L0 16.48 0 0 1200 0 1200 120z"
|
||||
className="custom-bg-dark1"
|
||||
></path>
|
||||
</svg>
|
||||
</div>
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
whileInView={{ opacity: 1 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.5, delay: 0.2 }}
|
||||
>
|
||||
<div className=" 2xl:w-[1150px] xl:w-[1050px] md:w-4/5 flex justify-center bg-customDarkBg1 pt-12 lg:pt-24 pb-8 lg:pb-20 mx-auto lg:flex-row flex-col">
|
||||
<div className="w-3/4 lg:w-1/2 flex flex-col lg:mx-unset mx-auto">
|
||||
<span className="custom-block-subtitle">
|
||||
Early Access
|
||||
</span>
|
||||
<h2 className="mt-10 mb-8 text-4xl lg:text-5xl custom-block-big-title">
|
||||
Run qsv commands with our qsv Configurator GUI
|
||||
</h2>
|
||||
<p className="mb-16 text-customGrayText leading-loose">
|
||||
No need to run qsv commands in your terminal. Try
|
||||
out our early access qsv Configurator GUI to use qsv
|
||||
commands with a graphical user interface.
|
||||
</p>
|
||||
<a
|
||||
href="https://store.dathere.com/checkout/buy/41f919fd-2b68-40ea-a5ed-0f531b2efba5"
|
||||
target="_blank"
|
||||
>
|
||||
<div className="w-[210px] h-12 custom-button-colored mr-10 ">
|
||||
Start Your Free Trial
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
<div className="w-4/5 lg:w-2/3 lg:pl-16 flex justify-center mx-auto pt-16 lg:pt-0">
|
||||
<img
|
||||
src={configurator}
|
||||
alt="Configurator Demo"
|
||||
className="rounded-xl custom-border-gray"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
<div className="custom-shape-divider-top-1665696661 w-full">
|
||||
<svg
|
||||
data-name="Layer 1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 1200 120"
|
||||
preserveAspectRatio="none"
|
||||
className="custom-bg-dark2"
|
||||
>
|
||||
<path
|
||||
d="M1200 120L0 16.48 0 0 1200 0 1200 120z"
|
||||
className="custom-bg-dark1"
|
||||
></path>
|
||||
</svg>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
126
src/components/Footer.jsx
Normal file
126
src/components/Footer.jsx
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
import { FacebookIcon } from "../assets/icons/FacebookIcon";
|
||||
import { InstagramIcon } from "../assets/icons/InstagramIcon";
|
||||
import datHereLogoFull from "../assets/logos/datHere-logo-light.png";
|
||||
import { TwitterIcon } from "../assets/icons/TwitterIcon";
|
||||
|
||||
const footerData = [
|
||||
{
|
||||
title: "Products",
|
||||
items: ["Services", "About Us", "News and Stories", "Roadmap"],
|
||||
},
|
||||
{
|
||||
title: "Important Links",
|
||||
items: [
|
||||
"Organization Team",
|
||||
"Our Journeys",
|
||||
"Pricing Plans",
|
||||
"Roadmap",
|
||||
"Terms & Conditions",
|
||||
"Privacy Policy",
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "Company",
|
||||
items: ["About Us", "Jobs", "Press", "Contact Us"],
|
||||
},
|
||||
];
|
||||
|
||||
export const Footer = () => {
|
||||
return (
|
||||
<footer>
|
||||
<div className="pt-10 lg:pt-20 lg:pb-12 bg-customDarkBg1 radius-for-skewed">
|
||||
<div className="container mx-auto px-4 w-4/5 md:w-11/12 lg:w-10/12 xl:w-4/5 2xl:w-2/3">
|
||||
<div className="flex flex-wrap">
|
||||
<div className="w-full lg:w-1/3 mb-16 lg:mb-0">
|
||||
<div className="flex justify-center lg:justify-start items-center grow basis-0">
|
||||
<div className="text-white mr-2 text-6xl">
|
||||
<img
|
||||
src={datHereLogoFull}
|
||||
alt="datHere Logo"
|
||||
className="w-48"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<p className="mb-10 mt-4 sm:w-[22rem] lg:w-[20rem] xl:w-[24rem] text-gray-400 leading-loose text-center lg:text-left mx-auto lg:mx-0">
|
||||
Making data Useful, Usable, and Used.
|
||||
</p>
|
||||
{/* <div className="w-36 mx-auto lg:mx-0">
|
||||
<a
|
||||
className="inline-block w-10 h-10 mr-2 p-2 bg-customDarkBg2 custom-border-gray hover:bg-gray-700 rounded-xl"
|
||||
href="#"
|
||||
>
|
||||
<TwitterIcon />
|
||||
</a>
|
||||
<a
|
||||
className="inline-block w-10 h-10 mr-2 p-2 bg-customDarkBg2 custom-border-gray hover:bg-gray-700 rounded-xl"
|
||||
href="#"
|
||||
>
|
||||
<InstagramIcon />
|
||||
</a>
|
||||
</div> */}
|
||||
</div>
|
||||
<div className="w-full lg:w-2/3 lg:pl-16 hidden lg:flex flex-wrap justify-between">
|
||||
{/* <div className="w-full md:w-1/3 lg:w-auto mb-16 md:mb-0">
|
||||
<h3 className="mb-6 text-2xl font-bold text-white">
|
||||
Products
|
||||
</h3>
|
||||
<ul>
|
||||
{footerData[0].items.map((item, i) => (
|
||||
<li key={i} className="mb-4">
|
||||
<a
|
||||
className="text-gray-400 hover:text-gray-300"
|
||||
href="#"
|
||||
aria-label=""
|
||||
>
|
||||
{item}
|
||||
</a>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div> */}
|
||||
{/* <div className="w-full md:w-1/3 lg:w-auto mb-16 md:mb-0">
|
||||
<h3 className="mb-6 text-2xl font-bold text-white">
|
||||
Important Links
|
||||
</h3>
|
||||
<ul>
|
||||
{footerData[1].items.map((item, i) => (
|
||||
<li key={i} className="mb-4">
|
||||
<a
|
||||
className="text-gray-400 hover:text-gray-300"
|
||||
href="#"
|
||||
aria-label=""
|
||||
>
|
||||
{item}
|
||||
</a>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div> */}
|
||||
{/* <div className="w-full md:w-1/3 lg:w-auto">
|
||||
<h3 className="mb-6 text-2xl font-bold text-white">
|
||||
Company
|
||||
</h3>
|
||||
<ul>
|
||||
{footerData[2].items.map((item, i) => (
|
||||
<li key={i} className="mb-4">
|
||||
<a
|
||||
className="text-gray-400 hover:text-gray-300"
|
||||
href="#"
|
||||
aria-label=""
|
||||
>
|
||||
{item}
|
||||
</a>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div> */}
|
||||
</div>
|
||||
</div>
|
||||
<p className="lg:text-center text-sm text-gray-400 border-t border-[rgb(255,255,255,0.2)] pt-4 hidden lg:block">
|
||||
© 2023 datHere Inc.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
);
|
||||
};
|
||||
110
src/components/Hero.jsx
Normal file
110
src/components/Hero.jsx
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
import { useState } from "react";
|
||||
import { motion } from "framer-motion";
|
||||
|
||||
import dashboard from "../assets/images/dashboard.png";
|
||||
|
||||
export const Hero = () => {
|
||||
return (
|
||||
<section
|
||||
className="w-screen flex justify-center items-center bg-customDarkBg1 mb-[28vw] md:mb-[18vw] lg:mb-[10vw] xl:mb-[13vw] 2xl:mb-60 hero-bg-gradient pb-24 sm:pb-32 md:pb-44 lg:pb-0"
|
||||
id="home"
|
||||
>
|
||||
<div className="w-full w-[900px] md:w-[1100px] flex flex-col justify-center items-center pt-16 md:pt-16 lg:pt-20 text-center">
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 10 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.5 }}
|
||||
>
|
||||
<div className="text-customSecondary text-sm sm:text-base mb-6 sm:mt-32 mt-16 font-bold">
|
||||
Spreadsheet Data Wrangling Desktop App
|
||||
</div>
|
||||
</motion.div>
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 10 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.5, delay: 0.05 }}
|
||||
>
|
||||
<div className="text-5xl sm:text-6xl lg:text-7xl xl:text-7xl font-bold tracking-wide text-white px-8 sm:px-8 md:px-20 lg:px-4">
|
||||
<span>qsv pro</span>
|
||||
</div>
|
||||
</motion.div>
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 10 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.5, delay: 0.1 }}
|
||||
>
|
||||
<div className="text-customGrayText text-sm lg:text-base xl:text-lg sm:text-base mt-10 px-12 sm:px-48 ">
|
||||
Transform and upload spreadsheet data to{" "}
|
||||
<a
|
||||
href="https://ckan.org"
|
||||
target="_blank"
|
||||
className="text-blue-300"
|
||||
>
|
||||
CKAN
|
||||
</a>{" "}
|
||||
with our streamlined desktop app, featuring "recipes"
|
||||
for common data wrangling tasks. Based on the{" "}
|
||||
<a
|
||||
href="https://github.com/jqnatividad/qsv"
|
||||
target="_blank"
|
||||
className="text-blue-300"
|
||||
>
|
||||
qsv
|
||||
</a>{" "}
|
||||
CLI tool.
|
||||
</div>
|
||||
</motion.div>
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 10 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.5, delay: 0.15 }}
|
||||
>
|
||||
<div className="flex flex-col gap-2 sm:flex-row mt-14 mb-24 sm:mb-40 justify-center">
|
||||
<a
|
||||
href="https://store.dathere.com/checkout/buy/41f919fd-2b68-40ea-a5ed-0f531b2efba5"
|
||||
target="_blank"
|
||||
>
|
||||
<div className="custom-button-colored w-64 sm:w-52 h-12 mr-0 sm:mr-4 lg:mr-6 mb-2 sm:mb-0">
|
||||
Start Your Free Trial
|
||||
</div>
|
||||
</a>
|
||||
{/* <div
|
||||
className="w-64 sm:w-52 h-12 rounded-xl font-bold text-white border border-solid flex justify-center items-center cursor-pointer bg-customDarkBg2 hover:bg-customDarkBg3 border-customPrimary transition"
|
||||
>
|
||||
Live demo
|
||||
</div> */}
|
||||
</div>
|
||||
</motion.div>
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 10, zIndex: 20 }}
|
||||
animate={{ opacity: 1, y: 0, zIndex: 20 }}
|
||||
transition={{ duration: 0.5, delay: 0.15 }}
|
||||
>
|
||||
<div className="relative w-screen flex justify-center ">
|
||||
<img
|
||||
src={dashboard}
|
||||
alt="123"
|
||||
className="w-4/5 2xl:w-[1200px] mx-auto absolute z-10 rounded-xl custom-border-gray hero-dashboard-border-gradient lg:top-6 xl:top-0"
|
||||
/>
|
||||
</div>
|
||||
</motion.div>
|
||||
<div className="relative w-screen flex justify-center ">
|
||||
<div className="custom-shape-divider-bottom-1665343298 mt-4 sm:mt-16 md:mt-52 hidden lg:block">
|
||||
<svg
|
||||
data-name="Layer 1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 1200 120"
|
||||
preserveAspectRatio="none"
|
||||
className=" bg-customDarkBg2"
|
||||
>
|
||||
<path
|
||||
d="M1200 0L0 0 598.97 114.72 1200 0z"
|
||||
className="shape-fill custom-bg-dark1"
|
||||
></path>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
129
src/components/Navbar.jsx
Normal file
129
src/components/Navbar.jsx
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
import { useState } from "react";
|
||||
import { motion, AnimatePresence } from "framer-motion";
|
||||
|
||||
import datHereLogo from "../assets/logos/datHereLogo.png";
|
||||
import { GithubIcon } from "../assets/icons/GithubIcon";
|
||||
|
||||
const navbarLinks = [
|
||||
{ label: "Home", href: "#home", ariaLabel: "Home" },
|
||||
{ label: "Features", href: "#features", ariaLabel: "Features" },
|
||||
// { label: "Feedback", href: "#feedback", ariaLabel: "Feedback" },
|
||||
{ label: "Pricing", href: "#pricing", ariaLabel: "Pricing" },
|
||||
// { label: "FAQ", href: "#FAQ", ariaLabel: "FAQ" },
|
||||
];
|
||||
|
||||
export const Navbar = () => {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<nav className="w-full h-20 flex flex-col justify-center items-center fixed bg-customDarkBg1 lg:bg-customDarkBgTransparent z-40 lg:backdrop-blur-xl">
|
||||
<div className="2xl:w-[1280px] xl:w-10/12 w-11/12 flex justify-between items-center relative">
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
transition={{ duration: 0.3 }}
|
||||
exit={{ opacity: 0 }}
|
||||
>
|
||||
<a className="navbar-link" href="#home" aria-label="Home">
|
||||
<div className="flex justify-start items-center grow basis-0">
|
||||
<div className="text-white mr-2 text-6xl">
|
||||
<img
|
||||
src={datHereLogo}
|
||||
alt="datHere logo"
|
||||
className="w-6 h-6"
|
||||
/>
|
||||
</div>
|
||||
<div className="text-white font-['Inter'] font-bold text-xl">
|
||||
qsv pro
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</motion.div>
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
transition={{ duration: 0.3 }}
|
||||
exit={{ opacity: 0 }}
|
||||
>
|
||||
<div className="hidden lg:flex h-full pl-12 pb-2">
|
||||
{navbarLinks.map(({ href, label, ariaLabel }) => (
|
||||
<a
|
||||
className="navbar-link"
|
||||
href={href}
|
||||
aria-label={ariaLabel}
|
||||
key={label}
|
||||
>
|
||||
{label}
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
</motion.div>
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
transition={{ duration: 0.3 }}
|
||||
exit={{ opacity: 0 }}
|
||||
>
|
||||
<div className="grow basis-0 justify-end hidden lg:flex">
|
||||
<a
|
||||
className="text-white custom-border-gray rounded-xl
|
||||
bg-customDarkBg2 hover:bg-customDarkBg3 border-gray-700 pl-6 pr-8 pt-2 pb-2 text-sm flex"
|
||||
href="https://github.com/dathere/qsv-pro-releases/releases/latest"
|
||||
target="_blank"
|
||||
aria-label="source code"
|
||||
>
|
||||
<GithubIcon />
|
||||
<span className="pt-px">Download</span>
|
||||
</a>
|
||||
</div>
|
||||
</motion.div>
|
||||
<div
|
||||
className="lg:hidden flex flex-col px-2 py-3 border-solid border border-gray-600 rounded-md cursor-pointer hover:bg-customDarkBg2"
|
||||
onClick={() => setIsOpen(!isOpen)}
|
||||
>
|
||||
<div className="w-5 h-0.5 bg-gray-500 mb-1"></div>
|
||||
<div className="w-5 h-0.5 bg-gray-500 mb-1"></div>
|
||||
<div className="w-5 h-0.5 bg-gray-500 "></div>
|
||||
</div>
|
||||
</div>
|
||||
{/* Mobile navbar */}
|
||||
<AnimatePresence>
|
||||
{isOpen && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
transition={{ duration: 0.3 }}
|
||||
exit={{ opacity: 0 }}
|
||||
>
|
||||
<div
|
||||
className="flex flex-col mt-16 lg:hidden absolute top-4 left-0 bg-customDarkBg1 z-50 w-full
|
||||
items-center gap-10 pb-10 border-y border-solid border-customDarkBg3 pt-10
|
||||
"
|
||||
>
|
||||
{navbarLinks.map(({ label, href, ariaLabel }) => (
|
||||
<a
|
||||
key={href}
|
||||
className="navbar-link"
|
||||
href={href}
|
||||
onClick={() => setIsOpen(false)}
|
||||
aria-label={ariaLabel}
|
||||
>
|
||||
{label}
|
||||
</a>
|
||||
))}
|
||||
<a
|
||||
className="text-white custom-border-gray rounded-xl
|
||||
bg-customDarkBg2 hover:bg-customDarkBg3 border-gray-700 pl-6 pr-8 pt-2 pb-2 text-sm flex"
|
||||
href="https://github.com/dathere/qsv-pro-releases/releases/latest"
|
||||
target="_blank"
|
||||
>
|
||||
<GithubIcon />
|
||||
Download
|
||||
</a>
|
||||
</div>
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</nav>
|
||||
);
|
||||
};
|
||||
194
src/components/Pricing.jsx
Normal file
194
src/components/Pricing.jsx
Normal file
|
|
@ -0,0 +1,194 @@
|
|||
import { useState } from "react";
|
||||
import { motion } from "framer-motion";
|
||||
|
||||
import { CheckArrowIcon } from "../assets/icons/CheckArrowIcon";
|
||||
|
||||
const pricingData = [
|
||||
"Import local spreadsheet data",
|
||||
"Transform data with recipes",
|
||||
"Upload to CKAN datastores",
|
||||
"Interactive data table view",
|
||||
"Early access to Configurator",
|
||||
];
|
||||
|
||||
export const Pricing = () => {
|
||||
const [isMonthly, setIsMonthly] = useState(true);
|
||||
|
||||
const handleChange = () => {
|
||||
setIsMonthly(!isMonthly);
|
||||
};
|
||||
|
||||
return (
|
||||
<section className="w-screen flex justify-center bg-customDarkBg2 relative">
|
||||
<div className="absolute -top-16" id="pricing" />
|
||||
<div className="pb-20 pt-12 bg-customDarkBg2 2xl:w-[1150px] lg:w-[1050px] md:w-4/5 ">
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
whileInView={{ opacity: 1 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.5, delay: 0.2 }}
|
||||
>
|
||||
<div className="container mx-auto px-4">
|
||||
<div className="max-w-2xl mx-auto text-center mb-16">
|
||||
<span className="custom-block-subtitle">
|
||||
Pick a Plan
|
||||
</span>
|
||||
<h2 className="mt-6 mb-6 text-4xl lg:text-5xl font-bold font-heading text-white">
|
||||
Choose your plan
|
||||
</h2>
|
||||
<p className="mb-6 text-customGrayText">
|
||||
Select the subscription plan that suits your
|
||||
needs and benefit from qsv pro.
|
||||
</p>
|
||||
<label className="mx-auto bg-customDarkBg3 relative flex justify-between items-center group text-xl w-44 h-12 rounded-lg pr-36 pl-1 cursor-pointer">
|
||||
<input
|
||||
type="checkbox"
|
||||
className="peer appearance-none"
|
||||
checked={!isMonthly}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
<span className="h-8 w-[5.5rem] flex items-center pr-2 bg-customDarkBg3 after:rounded-lg duration-300 ease-in-out after:w-[30rem] after:h-10 after:bg-customPrimary after:shadow-md after:duration-300 peer-checked:after:translate-x-[5.5rem] cursor-pointer"></span>
|
||||
<div className="flex absolute text-white text-sm font-bold">
|
||||
<div
|
||||
className={
|
||||
isMonthly
|
||||
? "mr-9 ml-3"
|
||||
: "mr-9 ml-3 text-gray-400"
|
||||
}
|
||||
>
|
||||
Monthly
|
||||
</div>
|
||||
<div
|
||||
className={isMonthly && "text-gray-400"}
|
||||
>
|
||||
Yearly
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
<div className="flex flex-wrap flex-col lg:flex-row -mx-4 items-center mt-20">
|
||||
<div className="w-[350px] sm:w-[380px] lg:w-1/3 px-4 mb-8 lg:mb-0">
|
||||
{/* <div className="p-8 bg-customDarkBg3 rounded-3xl">
|
||||
<h4 className="mb-2 text-xl font-bold font-heading text-white text-left">
|
||||
Beginner
|
||||
</h4>
|
||||
<div className="flex justify-start items-end">
|
||||
<div className="text-4xl sm:text-5xl font-bold text-white text-left mt-4 mr-2">
|
||||
$0
|
||||
</div>
|
||||
<div className="text-gray-500">
|
||||
{isMonthly ? "/ month" : "/ year"}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p className="mt-4 mb-6 2xl:mb-10 text-gray-500 leading-loose text-left">
|
||||
The perfect way to get started and get
|
||||
used to our tools.
|
||||
</p>
|
||||
<ul className="mb-2 2xl:mb-6 text-white">
|
||||
{pricingData.map((text, index) => (
|
||||
<li
|
||||
className="mb-4 flex"
|
||||
key={`${text}-${index}`}
|
||||
>
|
||||
<CheckArrowIcon />
|
||||
<span>{text}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
<div
|
||||
className="inline-block text-center py-2 px-4 w-full rounded-xl rounded-t-xl custom-button-colored font-bold leading-loose mt-16"
|
||||
>
|
||||
Get Started
|
||||
</div>
|
||||
</div> */}
|
||||
</div>
|
||||
<div className="w-[350px] sm:w-[380px] lg:w-1/3 px-4 mb-8 lg:mb-0">
|
||||
<div className="px-8 py-8 bg-customDarkBg3 rounded-3xl">
|
||||
<h4 className="mb-2 2xl:mb-4 text-2xl font-bold font-heading text-white text-left">
|
||||
qsv pro
|
||||
</h4>
|
||||
<div className="flex justify-start items-end">
|
||||
<div className="text-4xl sm:text-5xl font-bold text-white text-left mt-4 mr-2">
|
||||
{isMonthly ? "$99.99" : "$999.00"}
|
||||
</div>
|
||||
<div className="text-gray-500">
|
||||
{isMonthly ? "/ month" : "/ year"}
|
||||
</div>
|
||||
</div>
|
||||
<p className="mt-8 mb-8 2xl:mb-12 text-gray-500 leading-loose text-left">
|
||||
1 license key + 7 days free trial
|
||||
</p>
|
||||
<ul className="mb-14 text-white">
|
||||
{pricingData
|
||||
.concat(
|
||||
!isMonthly
|
||||
? ["~$200 saved per year!"]
|
||||
: []
|
||||
)
|
||||
.map((text, index) => (
|
||||
<li
|
||||
className="mb-4 flex"
|
||||
key={`${text}-${index}`}
|
||||
>
|
||||
<CheckArrowIcon />
|
||||
<span>{text}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
<a
|
||||
href={
|
||||
isMonthly
|
||||
? "https://store.dathere.com/checkout/buy/41f919fd-2b68-40ea-a5ed-0f531b2efba5"
|
||||
: "https://store.dathere.com/checkout/buy/88fed582-ffd4-41e0-a94e-457fdd038130"
|
||||
}
|
||||
target="_blank"
|
||||
>
|
||||
<div className="inline-block text-center py-2 px-4 w-full custom-button-colored leading-loose transition duration-200 mt-20">
|
||||
Start Your 7-Day Free Trial
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div className="w-[350px] sm:w-[380px] lg:w-1/3 px-4 mb-8 lg:mb-0">
|
||||
{/* <div className="p-8 bg-customDarkBg3 rounded-3xl">
|
||||
<h4 className="mb-2 text-xl font-bold font-heading text-white text-left">
|
||||
Premium
|
||||
</h4>
|
||||
<div className="flex justify-start items-end">
|
||||
<div className="text-4xl sm:text-5xl font-bold text-white text-left mt-4 mr-2">
|
||||
{isMonthly ? "$36" : "$390"}
|
||||
</div>
|
||||
<div className="text-gray-500">
|
||||
{isMonthly ? "/ month" : "/ year"}
|
||||
</div>
|
||||
</div>
|
||||
<p className="mt-4 mb-6 2xl:mb-10 text-gray-500 leading-loose text-left">
|
||||
Experience the full power of our
|
||||
analytic platform
|
||||
</p>
|
||||
<ul className="mb-2 2xl:mb-6 text-white">
|
||||
{pricingData.map((text, index) => (
|
||||
<li
|
||||
className="mb-4 flex"
|
||||
key={`${text}-${index}`}
|
||||
>
|
||||
<CheckArrowIcon />
|
||||
<span>{text}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
<div
|
||||
className="inline-block text-center py-2 px-4 w-full rounded-xl rounded-t-xl custom-button-colored font-bold leading-loose mt-16"
|
||||
>
|
||||
Get Started
|
||||
</div>
|
||||
</div> */}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
52
src/components/ScrollUpButton.jsx
Normal file
52
src/components/ScrollUpButton.jsx
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
import { useEffect, useState } from "react";
|
||||
|
||||
export const ScrollUpButton = () => {
|
||||
const [isVisible, setIsVisible] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
window.addEventListener("scroll", toggleVisible);
|
||||
}, []);
|
||||
|
||||
const toggleVisible = () => {
|
||||
const scrolled = document.documentElement.scrollTop;
|
||||
if (scrolled > 300) {
|
||||
setIsVisible(true);
|
||||
} else if (scrolled <= 300) {
|
||||
setIsVisible(false);
|
||||
}
|
||||
};
|
||||
|
||||
const scrollToTop = () => {
|
||||
window.scrollTo({
|
||||
top: 0,
|
||||
behavior: "smooth",
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{isVisible && (
|
||||
<div
|
||||
className="w-12 h-12 fixed bottom-6 right-6 custom-border-gray rounded-xl bg-customDarkBg2 hover:bg-customDarkBg3 cursor-pointer flex justify-center items-center transition z-50"
|
||||
onClick={scrollToTop}
|
||||
>
|
||||
<svg
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="35px"
|
||||
height="35px"
|
||||
viewBox="0 0 20 20"
|
||||
>
|
||||
<path
|
||||
d="M4.16732 12.5L10.0007 6.66667L15.834 12.5"
|
||||
stroke="#007AFF"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
></path>
|
||||
</svg>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
62
src/components/Tech.jsx
Normal file
62
src/components/Tech.jsx
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
import { motion } from "framer-motion";
|
||||
|
||||
import { TauriLogo } from "../assets/logos/TauriLogo";
|
||||
import { RustLogo } from "../assets/logos/RustLogo";
|
||||
import { TypescriptLogo } from "../assets/logos/TypescriptLogo";
|
||||
import { NextLogo } from "../assets/logos/NextLogo";
|
||||
|
||||
export const Tech = () => (
|
||||
<section className="py-12 sm:py-24 bg-customDarkBg1 w-full mt-16 mb-16">
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
whileInView={{ opacity: 1 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.5, delay: 0.2 }}
|
||||
>
|
||||
<div className="container px-4 mx-auto 2xl:w-[1200px] xl:w-[1100px] lg:w-[1000px] md:w-4/5">
|
||||
<div className="flex lg:flex-row flex-col items-center -mx-4 justify-center lg:text-left text-center">
|
||||
<div className="w-full lg:w-1/2 px-4 mb-12 lg:mb-0">
|
||||
<div className="flex flex-col">
|
||||
<h2 className="mb-2 text-4xl sm:text-5xl 2xl:text-6xl font-bold tracking-normal text-white">
|
||||
Built with
|
||||
</h2>
|
||||
<h2 className=" text-4xl sm:text-5xl 2xl:text-6xl font-bold tracking-normal text-customSecondary">
|
||||
modern tech
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
<div className="w-2/3 sm:w-[620px] lg:w-1/2 mx-auto lg:mx-0 lg:pl-10">
|
||||
<div className="flex flex-wrap -m-4">
|
||||
<div className="w-1/2 sm:w-1/4 py-6 flex justify-center">
|
||||
<a href="https://tauri.app" target="_blank">
|
||||
<TauriLogo />
|
||||
</a>
|
||||
</div>
|
||||
<div className="w-1/2 sm:w-1/4 py-6 flex justify-center">
|
||||
<a
|
||||
href="https://www.typescriptlang.org/"
|
||||
target="_blank"
|
||||
>
|
||||
<TypescriptLogo />
|
||||
</a>
|
||||
</div>
|
||||
<div className="w-1/2 sm:w-1/4 py-6 flex justify-center">
|
||||
<a
|
||||
href="https://www.rust-lang.org/"
|
||||
target="_blank"
|
||||
>
|
||||
<RustLogo />
|
||||
</a>
|
||||
</div>
|
||||
<div className="w-1/2 sm:w-1/4 py-6 flex justify-center">
|
||||
<a href="https://nextjs.org/" target="_blank">
|
||||
<NextLogo />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
</section>
|
||||
);
|
||||
82
src/components/Testimonials.jsx
Normal file
82
src/components/Testimonials.jsx
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
import { motion } from "framer-motion";
|
||||
|
||||
import { QuoteIcon } from "../assets/icons/QuoteIcon";
|
||||
import testimonial1 from "../assets/images/testimonial1.png";
|
||||
import testimonial2 from "../assets/images/testimonial2.png";
|
||||
import testimonial3 from "../assets/images/testimonial3.png";
|
||||
|
||||
const testimonialsData = [
|
||||
{
|
||||
customerName: "John Watkins",
|
||||
customerTitle: "Founder of Dashflow",
|
||||
content:
|
||||
"The powerful analytic tools have helped us streamline our processes and make data-driven decisions that positively impact our efficiency. Tailcast has been a game-changer for our business. The platform is easy to use, and the insights we've gained have driven significant improvements.",
|
||||
image: testimonial1,
|
||||
},
|
||||
{
|
||||
customerName: "John Watkins",
|
||||
customerTitle: "Founder of Dashflow",
|
||||
content:
|
||||
"The powerful analytic tools have helped us streamline our processes and make data-driven decisions that positively impact our efficiency. Tailcast has been a game-changer for our business. The platform is easy to use, and the insights we've gained have driven significant improvements.",
|
||||
image: testimonial2,
|
||||
},
|
||||
{
|
||||
customerName: "John Watkins",
|
||||
customerTitle: "Founder of Dashflow",
|
||||
content:
|
||||
"The powerful analytic tools have helped us streamline our processes and make data-driven decisions that positively impact our efficiency. Tailcast has been a game-changer for our business. The platform is easy to use, and the insights we've gained have driven significant improvements.",
|
||||
image: testimonial3,
|
||||
},
|
||||
];
|
||||
|
||||
export const Testimonials = () => (
|
||||
<section className="w-full flex justify-center pt-10 mb-16 lg:mb-32 bg-customDarkBg2 relative">
|
||||
<div className="absolute -top-16" id="feedback" />
|
||||
<div className="flex flex-col w-full lg:w-[1150px] justify-center">
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
whileInView={{ opacity: 1 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.4, delay: 0.3 }}
|
||||
>
|
||||
{/* <div className="custom-block-subtitle text-center mb-6">
|
||||
Testimonials
|
||||
</div> */}
|
||||
<div className="custom-block-big-title text-center mb-16 px-8 sm:px-24 md:px-48">
|
||||
Testimonials
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col lg:flex-row gap-8 lg:gap-5 xl:gap-10 px-6 xl:px-0 items-center">
|
||||
{testimonialsData.map((testimonial, index) => (
|
||||
<div
|
||||
className="w-11/12 sm:w-4/5 md:w-[560px] lg:w-1/3 custom-border-gray-darker rounded-xl bg-customDarkBg3 flex flex-col px-6 py-4"
|
||||
key={`${testimonial.customerName}-${index}`}
|
||||
>
|
||||
<div className="flex mb-2">
|
||||
<QuoteIcon />
|
||||
</div>
|
||||
<div className="custom-content-text-white">
|
||||
"{testimonial.content}"
|
||||
</div>
|
||||
<div className="flex mt-4 mb-2 xl:mt-8 xl:mb-4">
|
||||
<img
|
||||
src={testimonial.image}
|
||||
alt=""
|
||||
width="45px"
|
||||
/>
|
||||
<div className="flex flex-col ml-4">
|
||||
<div className="custom-content-text-white font-medium">
|
||||
{testimonial.customerName}
|
||||
</div>
|
||||
<div className="custom-content-text-gray">
|
||||
{testimonial.customerTitle}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</motion.div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
Loading…
Add table
Add a link
Reference in a new issue