style: run prettier
This commit is contained in:
parent
2193d68895
commit
d80c0ec7de
27 changed files with 698 additions and 569 deletions
|
|
@ -1,64 +1,60 @@
|
||||||
import Link from 'next/link'
|
import Link from "next/link";
|
||||||
import clsx from 'clsx'
|
import clsx from "clsx";
|
||||||
import { ReactNode } from 'react'
|
import { ReactNode } from "react";
|
||||||
|
|
||||||
const baseStyles = {
|
const baseStyles = {
|
||||||
solid:
|
solid:
|
||||||
'group inline-flex items-center justify-center rounded-full py-2 px-4 text-sm font-semibold focus:outline-none focus-visible:outline-2 focus-visible:outline-offset-2',
|
"group inline-flex items-center justify-center rounded-full py-2 px-4 text-sm font-semibold focus:outline-none focus-visible:outline-2 focus-visible:outline-offset-2",
|
||||||
outline:
|
outline:
|
||||||
'group inline-flex ring-1 items-center justify-center rounded-full py-2 px-4 text-sm focus:outline-none',
|
"group inline-flex ring-1 items-center justify-center rounded-full py-2 px-4 text-sm focus:outline-none",
|
||||||
}
|
};
|
||||||
|
|
||||||
const variantStyles = {
|
const variantStyles = {
|
||||||
solid: {
|
solid: {
|
||||||
slate:
|
slate:
|
||||||
'bg-slate-900 text-white hover:bg-slate-700 hover:text-slate-100 active:bg-slate-800 active:text-slate-300 focus-visible:outline-slate-900',
|
"bg-slate-900 text-white hover:bg-slate-700 hover:text-slate-100 active:bg-slate-800 active:text-slate-300 focus-visible:outline-slate-900",
|
||||||
blue: 'bg-blue-600 text-white hover:text-slate-100 hover:bg-blue-500 active:bg-blue-800 active:text-blue-100 focus-visible:outline-blue-600',
|
blue: "bg-blue-600 text-white hover:text-slate-100 hover:bg-blue-500 active:bg-blue-800 active:text-blue-100 focus-visible:outline-blue-600",
|
||||||
white:
|
white:
|
||||||
'bg-white text-slate-900 hover:bg-blue-50 active:bg-blue-200 active:text-slate-600 focus-visible:outline-white',
|
"bg-white text-slate-900 hover:bg-blue-50 active:bg-blue-200 active:text-slate-600 focus-visible:outline-white",
|
||||||
},
|
},
|
||||||
outline: {
|
outline: {
|
||||||
slate:
|
slate:
|
||||||
'ring-slate-200 text-slate-700 hover:text-slate-900 hover:ring-slate-300 active:bg-slate-100 active:text-slate-600 focus-visible:outline-blue-600 focus-visible:ring-slate-300',
|
"ring-slate-200 text-slate-700 hover:text-slate-900 hover:ring-slate-300 active:bg-slate-100 active:text-slate-600 focus-visible:outline-blue-600 focus-visible:ring-slate-300",
|
||||||
white:
|
white:
|
||||||
'ring-slate-700 text-white hover:ring-slate-500 active:ring-slate-700 active:text-slate-400 focus-visible:outline-white',
|
"ring-slate-700 text-white hover:ring-slate-500 active:ring-slate-700 active:text-slate-400 focus-visible:outline-white",
|
||||||
blue: // Supress TypeScript error
|
// Supress TypeScript error
|
||||||
'', // TODO: fix properly
|
blue: "", // TODO: fix properly
|
||||||
},
|
},
|
||||||
}
|
};
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
variant?: 'solid'|'outline',
|
variant?: "solid" | "outline";
|
||||||
color?: 'slate'|'white'|'blue',
|
color?: "slate" | "white" | "blue";
|
||||||
className?: string,
|
className?: string;
|
||||||
href?: string,
|
href?: string;
|
||||||
children?: ReactNode,
|
children?: ReactNode;
|
||||||
}
|
};
|
||||||
|
|
||||||
export function Button({
|
export function Button({
|
||||||
variant = 'solid',
|
variant = "solid",
|
||||||
color = 'slate',
|
color = "slate",
|
||||||
className,
|
className,
|
||||||
href,
|
href,
|
||||||
children
|
children,
|
||||||
}: Props) {
|
}: Props) {
|
||||||
className = clsx(
|
className = clsx(
|
||||||
baseStyles[variant],
|
baseStyles[variant],
|
||||||
variantStyles[variant][color],
|
variantStyles[variant][color],
|
||||||
className
|
className
|
||||||
)
|
);
|
||||||
|
|
||||||
if (href !== undefined) {
|
if (href !== undefined) {
|
||||||
return (
|
return (
|
||||||
<Link href={href} className={className}>
|
<Link href={href} className={className}>
|
||||||
{children}
|
{children}
|
||||||
</Link>
|
</Link>
|
||||||
)
|
);
|
||||||
} else {
|
} else {
|
||||||
return (
|
return <button className={className}>{children}</button>;
|
||||||
<button className={className}>
|
|
||||||
{children}
|
|
||||||
</button>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
import Image from 'next/image'
|
import Image from "next/image";
|
||||||
|
|
||||||
import { Button } from '@/components/Button'
|
import { Button } from "@/components/Button";
|
||||||
import { Container } from '@/components/Container'
|
import { Container } from "@/components/Container";
|
||||||
import backgroundImage from '@/images/background-call-to-action.jpg'
|
import backgroundImage from "@/images/background-call-to-action.jpg";
|
||||||
|
|
||||||
export function CallToAction() {
|
export function CallToAction() {
|
||||||
return (
|
return (
|
||||||
|
|
@ -24,14 +24,19 @@ export function CallToAction() {
|
||||||
Posuňte svoji aplikaci už dnes
|
Posuňte svoji aplikaci už dnes
|
||||||
</h2>
|
</h2>
|
||||||
<p className="mt-4 text-lg tracking-tight text-white">
|
<p className="mt-4 text-lg tracking-tight text-white">
|
||||||
Je na čase pozvednout vaši infrastrukturu na dnešní standardy.
|
Je na čase pozvednout vaši infrastrukturu na dnešní standardy. Vaše
|
||||||
Vaše infrastruktura by měla nabídnout Vaši aplikaci světu, ne ji držet zpátky.
|
infrastruktura by měla nabídnout Vaši aplikaci světu, ne ji držet
|
||||||
|
zpátky.
|
||||||
</p>
|
</p>
|
||||||
<Button href="https://calendly.com/vojtechmares/30min" color="white" className="mt-10">
|
<Button
|
||||||
|
href="https://calendly.com/vojtechmares/30min"
|
||||||
|
color="white"
|
||||||
|
className="mt-10"
|
||||||
|
>
|
||||||
Domluvme si schůzku
|
Domluvme si schůzku
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</Container>
|
</Container>
|
||||||
</section>
|
</section>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,15 @@
|
||||||
import clsx from 'clsx'
|
import clsx from "clsx";
|
||||||
import { ReactNode } from 'react'
|
import { ReactNode } from "react";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
className?: string,
|
className?: string;
|
||||||
children?: ReactNode
|
children?: ReactNode;
|
||||||
}
|
};
|
||||||
|
|
||||||
export function Container({ className, children }: Props) {
|
export function Container({ className, children }: Props) {
|
||||||
return (
|
return (
|
||||||
<div
|
<div className={clsx("mx-auto max-w-7xl px-4 sm:px-6 lg:px-8", className)}>
|
||||||
className={clsx('mx-auto max-w-7xl px-4 sm:px-6 lg:px-8', className)}
|
|
||||||
>
|
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,49 +1,48 @@
|
||||||
import Image from 'next/image'
|
import Image from "next/image";
|
||||||
|
|
||||||
import { Container } from './Container'
|
import { Container } from "./Container";
|
||||||
|
|
||||||
import logoKubernetes from '@/images/logos/tools/kubernetes.svg'
|
import logoKubernetes from "@/images/logos/tools/kubernetes.svg";
|
||||||
import logok3s from '@/images/logos/tools/k3s.svg'
|
import logok3s from "@/images/logos/tools/k3s.svg";
|
||||||
import logoRancher from '@/images/logos/tools/rancher.svg'
|
import logoRancher from "@/images/logos/tools/rancher.svg";
|
||||||
import logoAWS from '@/images/logos/tools/amazonaws.svg'
|
import logoAWS from "@/images/logos/tools/amazonaws.svg";
|
||||||
import logoDigitalOcean from '@/images/logos/tools/digitalocean.svg'
|
import logoDigitalOcean from "@/images/logos/tools/digitalocean.svg";
|
||||||
import logovmware from '@/images/logos/tools/vmware.svg'
|
import logovmware from "@/images/logos/tools/vmware.svg";
|
||||||
|
|
||||||
const features = [
|
const features = [
|
||||||
{
|
{
|
||||||
name: 'Kubernetes',
|
name: "Kubernetes",
|
||||||
summary: 'Open Source systém pro automatizaci deploymentů, škálování a správu kontejnerizovaných aplikací.',
|
summary:
|
||||||
|
"Open Source systém pro automatizaci deploymentů, škálování a správu kontejnerizovaných aplikací.",
|
||||||
description:
|
description:
|
||||||
'Kubernetes je na platformě nezávislý systém, díky kterému může běžet vaše aplikace u vás v datacentru, v public cloudu nebo třeba v okrajových lokalitách, a nebo třeba na všech najednou, bez problému.',
|
"Kubernetes je na platformě nezávislý systém, díky kterému může běžet vaše aplikace u vás v datacentru, v public cloudu nebo třeba v okrajových lokalitách, a nebo třeba na všech najednou, bez problému.",
|
||||||
icon: logoKubernetes,
|
icon: logoKubernetes,
|
||||||
iconColor: '#326CE5',
|
iconColor: "#326CE5",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'k3s & rke2',
|
name: "k3s & rke2",
|
||||||
summary:
|
summary:
|
||||||
'k3s: lehká distrubuce Kubernetes; rke2: Kubernetes do vašeho datacentra.',
|
"k3s: lehká distrubuce Kubernetes; rke2: Kubernetes do vašeho datacentra.",
|
||||||
description:
|
description:
|
||||||
'k3s i rke2 jsou velice snadno instalovatelné distribuce Kubernetes, které můžete nainstalovat úplně všude a přitom mít stále k dispozici celý Kubernetes ekosystém, bez kompromisů.',
|
"k3s i rke2 jsou velice snadno instalovatelné distribuce Kubernetes, které můžete nainstalovat úplně všude a přitom mít stále k dispozici celý Kubernetes ekosystém, bez kompromisů.",
|
||||||
icon: logok3s,
|
icon: logok3s,
|
||||||
iconColor: '#FFC61C',
|
iconColor: "#FFC61C",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Rancher',
|
name: "Rancher",
|
||||||
summary:
|
summary: "Kubernetes jako služba, ve vašem datacentru.",
|
||||||
'Kubernetes jako služba, ve vašem datacentru.',
|
|
||||||
description:
|
description:
|
||||||
'Platforma, pro vaše Kubernetes clustery. Komplexní řešení celého životního cyklu clusteru. Neřešte každodení problémy, nechte je řešit Rancher řešit za vás.',
|
"Platforma, pro vaše Kubernetes clustery. Komplexní řešení celého životního cyklu clusteru. Neřešte každodení problémy, nechte je řešit Rancher řešit za vás.",
|
||||||
icon: logoRancher,
|
icon: logoRancher,
|
||||||
iconColor: '#0075A8',
|
iconColor: "#0075A8",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'AWS',
|
name: "AWS",
|
||||||
summary:
|
summary: "Největší veřejný cloud. Máte problém? AWS má na to službu.",
|
||||||
'Největší veřejný cloud. Máte problém? AWS má na to službu.',
|
|
||||||
description:
|
description:
|
||||||
'Amazon Web Services (AWS) je cloudový poskytovatel služeb, včetně ukládání obrovských objemů dat, výpočetního výkonu a sítí. To vše aby pomohli firmám i jednotlivcům růst. AWS je nejrozšířenější poskytovatel, který je cenově dostupný, flexibilní cesta, jak stavět a provozovat aplikace a služby.',
|
"Amazon Web Services (AWS) je cloudový poskytovatel služeb, včetně ukládání obrovských objemů dat, výpočetního výkonu a sítí. To vše aby pomohli firmám i jednotlivcům růst. AWS je nejrozšířenější poskytovatel, který je cenově dostupný, flexibilní cesta, jak stavět a provozovat aplikace a služby.",
|
||||||
icon: logoAWS,
|
icon: logoAWS,
|
||||||
iconColor: '#232F3E',
|
iconColor: "#232F3E",
|
||||||
},
|
},
|
||||||
// {
|
// {
|
||||||
// name: 'Google Cloud Platform',
|
// name: 'Google Cloud Platform',
|
||||||
|
|
@ -55,13 +54,12 @@ const features = [
|
||||||
// iconColor: '#4285F4',
|
// iconColor: '#4285F4',
|
||||||
// },
|
// },
|
||||||
{
|
{
|
||||||
name: 'DigitalOcean',
|
name: "DigitalOcean",
|
||||||
summary:
|
summary: "Jednoduchý cloudový poskytovatel, ve kterém se neztratíte.",
|
||||||
'Jednoduchý cloudový poskytovatel, ve kterém se neztratíte.',
|
|
||||||
description:
|
description:
|
||||||
'DigitalOcean patří k menším cloudovým poskytovatelům, avšak jejich portfolio vám pro vaši aplikaci bohatě stačí a nebudete se ztrácet v komplexitě velkých poskytovatelů se spoustou služeb.',
|
"DigitalOcean patří k menším cloudovým poskytovatelům, avšak jejich portfolio vám pro vaši aplikaci bohatě stačí a nebudete se ztrácet v komplexitě velkých poskytovatelů se spoustou služeb.",
|
||||||
icon: logoDigitalOcean,
|
icon: logoDigitalOcean,
|
||||||
iconColor: '#0080FF',
|
iconColor: "#0080FF",
|
||||||
},
|
},
|
||||||
// {
|
// {
|
||||||
// name: 'OpenStack',
|
// name: 'OpenStack',
|
||||||
|
|
@ -73,50 +71,48 @@ const features = [
|
||||||
// iconColor: '#ED1944',
|
// iconColor: '#ED1944',
|
||||||
// },
|
// },
|
||||||
{
|
{
|
||||||
name: 'VMware',
|
name: "VMware",
|
||||||
summary:
|
summary:
|
||||||
'Populární řešení pro správu vaše datacentra, od virtuálních serverů až po úložiště.',
|
"Populární řešení pro správu vaše datacentra, od virtuálních serverů až po úložiště.",
|
||||||
description:
|
description:
|
||||||
'VMware je virtualizační platforma, která umožňuje vytvářet a spravovat virtuální servery a jejich fyzickém hardwaru. Zároveň umožňuje vytvářet oddělená prostředí například pro vývoj a ostrý provoz, tak i pro zcela různé aplikace.',
|
"VMware je virtualizační platforma, která umožňuje vytvářet a spravovat virtuální servery a jejich fyzickém hardwaru. Zároveň umožňuje vytvářet oddělená prostředí například pro vývoj a ostrý provoz, tak i pro zcela různé aplikace.",
|
||||||
icon: logovmware,
|
icon: logovmware,
|
||||||
iconColor: '#607078',
|
iconColor: "#607078",
|
||||||
},
|
},
|
||||||
]
|
];
|
||||||
|
|
||||||
type FeatureType = {
|
type FeatureType = {
|
||||||
name: string,
|
name: string;
|
||||||
summary: string,
|
summary: string;
|
||||||
description: string,
|
description: string;
|
||||||
icon: any,
|
icon: any;
|
||||||
iconColor: string,
|
iconColor: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
type FeatureProps = {
|
type FeatureProps = {
|
||||||
feature: FeatureType
|
feature: FeatureType;
|
||||||
className?: string,
|
className?: string;
|
||||||
props?: any[],
|
props?: any[];
|
||||||
}
|
};
|
||||||
|
|
||||||
function Feature({ feature, className, ...props }: FeatureProps) {
|
function Feature({ feature, className, ...props }: FeatureProps) {
|
||||||
return (
|
return (
|
||||||
<div
|
<div className={className} {...props}>
|
||||||
className={className}
|
<Image
|
||||||
{...props}
|
src={feature.icon}
|
||||||
>
|
className="rounded-lg p-2"
|
||||||
<Image src={feature.icon} className="rounded-lg p-2" width="128" height="128" color={feature.iconColor} alt="" />
|
width="128"
|
||||||
<h3
|
height="128"
|
||||||
className="mt-6 text-lg font-medium text-blue-600"
|
color={feature.iconColor}
|
||||||
>
|
alt=""
|
||||||
{feature.name}
|
/>
|
||||||
</h3>
|
<h3 className="mt-6 text-lg font-medium text-blue-600">{feature.name}</h3>
|
||||||
<p className="mt-2 font-display text-xl text-slate-900">
|
<p className="font-display mt-2 text-xl text-slate-900">
|
||||||
{feature.summary}
|
{feature.summary}
|
||||||
</p>
|
</p>
|
||||||
<p className="mt-4 text-sm text-slate-600">
|
<p className="mt-4 text-sm text-slate-600">{feature.description}</p>
|
||||||
{feature.description}
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function FeaturesMobile() {
|
function FeaturesMobile() {
|
||||||
|
|
@ -128,7 +124,7 @@ function FeaturesMobile() {
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function FeaturesDesktop() {
|
function FeaturesDesktop() {
|
||||||
|
|
@ -142,7 +138,7 @@ function FeaturesDesktop() {
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Environment() {
|
export function Environment() {
|
||||||
|
|
@ -158,12 +154,13 @@ export function Environment() {
|
||||||
Jakákoliv platforma, kdekoliv
|
Jakákoliv platforma, kdekoliv
|
||||||
</h2>
|
</h2>
|
||||||
<p className="mt-4 text-lg tracking-tight text-slate-700">
|
<p className="mt-4 text-lg tracking-tight text-slate-700">
|
||||||
Od veřejného cloudu přes on-premise až po serverless, se vším vám poradím.
|
Od veřejného cloudu přes on-premise až po serverless, se vším vám
|
||||||
|
poradím.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<FeaturesMobile />
|
<FeaturesMobile />
|
||||||
<FeaturesDesktop />
|
<FeaturesDesktop />
|
||||||
</Container>
|
</Container>
|
||||||
</section>
|
</section>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,61 +1,61 @@
|
||||||
import Image from 'next/image'
|
import Image from "next/image";
|
||||||
|
|
||||||
import { Container } from '@/components/Container'
|
import { Container } from "@/components/Container";
|
||||||
import backgroundImage from '@/images/background-faqs.jpg'
|
import backgroundImage from "@/images/background-faqs.jpg";
|
||||||
|
|
||||||
const faqs = [
|
const faqs = [
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
question: 'Does TaxPal handle VAT?',
|
question: "Does TaxPal handle VAT?",
|
||||||
answer:
|
answer:
|
||||||
'Well no, but if you move your company offshore you can probably ignore it.',
|
"Well no, but if you move your company offshore you can probably ignore it.",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
question: 'Can I pay for my subscription via purchase order?',
|
question: "Can I pay for my subscription via purchase order?",
|
||||||
answer: 'Absolutely, we are happy to take your money in all forms.',
|
answer: "Absolutely, we are happy to take your money in all forms.",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
question: 'How do I apply for a job at TaxPal?',
|
question: "How do I apply for a job at TaxPal?",
|
||||||
answer:
|
answer:
|
||||||
'We only hire our customers, so subscribe for a minimum of 6 months and then let’s talk.',
|
"We only hire our customers, so subscribe for a minimum of 6 months and then let’s talk.",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
question: 'What was that testimonial about tax fraud all about?',
|
question: "What was that testimonial about tax fraud all about?",
|
||||||
answer:
|
answer:
|
||||||
'TaxPal is just a software application, ultimately your books are your responsibility.',
|
"TaxPal is just a software application, ultimately your books are your responsibility.",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
question:
|
question:
|
||||||
'TaxPal sounds horrible but why do I still feel compelled to purchase?',
|
"TaxPal sounds horrible but why do I still feel compelled to purchase?",
|
||||||
answer:
|
answer:
|
||||||
'This is the power of excellent visual design. You just can’t resist it, no matter how poorly it actually functions.',
|
"This is the power of excellent visual design. You just can’t resist it, no matter how poorly it actually functions.",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
question:
|
question:
|
||||||
'I found other companies called TaxPal, are you sure you can use this name?',
|
"I found other companies called TaxPal, are you sure you can use this name?",
|
||||||
answer:
|
answer:
|
||||||
'Honestly not sure at all. We haven’t actually incorporated or anything, we just thought it sounded cool and made this website.',
|
"Honestly not sure at all. We haven’t actually incorporated or anything, we just thought it sounded cool and made this website.",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
question: 'How do you generate reports?',
|
question: "How do you generate reports?",
|
||||||
answer:
|
answer:
|
||||||
'You just tell us what data you need a report for, and we get our kids to create beautiful charts for you using only the finest crayons.',
|
"You just tell us what data you need a report for, and we get our kids to create beautiful charts for you using only the finest crayons.",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
question: 'Can we expect more inventory features?',
|
question: "Can we expect more inventory features?",
|
||||||
answer: 'In life it’s really better to never expect anything at all.',
|
answer: "In life it’s really better to never expect anything at all.",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
question: 'I lost my password, how do I get into my account?',
|
question: "I lost my password, how do I get into my account?",
|
||||||
answer:
|
answer:
|
||||||
'Send us an email and we will send you a copy of our latest password spreadsheet so you can find your information.',
|
"Send us an email and we will send you a copy of our latest password spreadsheet so you can find your information.",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
]
|
];
|
||||||
|
|
||||||
export function Faqs() {
|
export function Faqs() {
|
||||||
return (
|
return (
|
||||||
|
|
@ -106,5 +106,5 @@ export function Faqs() {
|
||||||
</ul>
|
</ul>
|
||||||
</Container>
|
</Container>
|
||||||
</section>
|
</section>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
import Link from 'next/link'
|
import Link from "next/link";
|
||||||
|
|
||||||
import { Container } from '@/components/Container'
|
import { Container } from "@/components/Container";
|
||||||
// import { Logo } from '@/components/Logo'
|
// import { Logo } from '@/components/Logo'
|
||||||
// import { NavLink } from '@/components/NavLink'
|
// import { NavLink } from '@/components/NavLink'
|
||||||
import { Button } from '@/components/Button'
|
import { Button } from "@/components/Button";
|
||||||
|
|
||||||
export function Footer() {
|
export function Footer() {
|
||||||
return (
|
return (
|
||||||
|
|
@ -13,19 +13,28 @@ export function Footer() {
|
||||||
<div className="grid grid-cols-1 gap-y-6 lg:grid-cols-4 lg:gap-4">
|
<div className="grid grid-cols-1 gap-y-6 lg:grid-cols-4 lg:gap-4">
|
||||||
<div>
|
<div>
|
||||||
<h4 className="text-lg font-medium">Vojtěch Mareš</h4>
|
<h4 className="text-lg font-medium">Vojtěch Mareš</h4>
|
||||||
<ul className="list-none mt-4">
|
<ul className="mt-4 list-none">
|
||||||
<li>
|
<li>
|
||||||
<Link href="tel:+420732490651" className="underline">+420 732 490 651</Link>
|
<Link href="tel:+420732490651" className="underline">
|
||||||
|
+420 732 490 651
|
||||||
|
</Link>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<Link href="mailto:iam@vojtechmares.com" className="underline">iam@vojtechmares.com</Link>
|
<Link
|
||||||
|
href="mailto:iam@vojtechmares.com"
|
||||||
|
className="underline"
|
||||||
|
>
|
||||||
|
iam@vojtechmares.com
|
||||||
|
</Link>
|
||||||
</li>
|
</li>
|
||||||
<li className="mt-4">
|
<li className="mt-4">
|
||||||
Company ID<br />
|
Company ID
|
||||||
|
<br />
|
||||||
<code id="company-id">06999280</code>
|
<code id="company-id">06999280</code>
|
||||||
</li>
|
</li>
|
||||||
<li className="mt-2">
|
<li className="mt-2">
|
||||||
VAT ID<br />
|
VAT ID
|
||||||
|
<br />
|
||||||
<code id="vat-id">CZ9709180063</code>
|
<code id="vat-id">CZ9709180063</code>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
@ -34,13 +43,17 @@ export function Footer() {
|
||||||
<h3 className="text-lg font-medium">Nejblíbenější školení</h3>
|
<h3 className="text-lg font-medium">Nejblíbenější školení</h3>
|
||||||
<ul className="mt-4 list-disc pl-4">
|
<ul className="mt-4 list-disc pl-4">
|
||||||
<li>
|
<li>
|
||||||
<Link href="/skoleni/kubernetes" className="underline">Kubernetes</Link>
|
<Link href="/skoleni/kubernetes" className="underline">
|
||||||
|
Kubernetes
|
||||||
|
</Link>
|
||||||
</li>
|
</li>
|
||||||
{/* <li>
|
{/* <li>
|
||||||
<Link href="/skoleni/gitlab-ci" className="underline">GitLab CI</Link>
|
<Link href="/skoleni/gitlab-ci" className="underline">GitLab CI</Link>
|
||||||
</li> */}
|
</li> */}
|
||||||
<li>
|
<li>
|
||||||
<Link href="/skoleni/terraform" className="underline">Terraform</Link>
|
<Link href="/skoleni/terraform" className="underline">
|
||||||
|
Terraform
|
||||||
|
</Link>
|
||||||
</li>
|
</li>
|
||||||
{/* <li>
|
{/* <li>
|
||||||
<Link href="/skoleni/rancher" className="underline">Rancher</Link>
|
<Link href="/skoleni/rancher" className="underline">Rancher</Link>
|
||||||
|
|
@ -85,7 +98,10 @@ export function Footer() {
|
||||||
Zaujal jsem vás avšak nevíte, jak přesně bych vám mohl pomoci?
|
Zaujal jsem vás avšak nevíte, jak přesně bych vám mohl pomoci?
|
||||||
Nebojte se zeptat a společně vymyslíme, jak vám mohu pomoci.
|
Nebojte se zeptat a společně vymyslíme, jak vám mohu pomoci.
|
||||||
</p>
|
</p>
|
||||||
<Button href="https://calendly.com/vojtechmares/30min" className="mt-5">
|
<Button
|
||||||
|
href="https://calendly.com/vojtechmares/30min"
|
||||||
|
className="mt-5"
|
||||||
|
>
|
||||||
Domluvme si schůzku
|
Domluvme si schůzku
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -137,10 +153,11 @@ export function Footer() {
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
<p className="mt-6 text-sm text-slate-500 sm:mt-0">
|
<p className="mt-6 text-sm text-slate-500 sm:mt-0">
|
||||||
Copyright © {new Date().getFullYear()} Vojtěch Mareš. Všechna práva vyhrazena.
|
Copyright © {new Date().getFullYear()} Vojtěch Mareš. Všechna
|
||||||
|
práva vyhrazena.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</Container>
|
</Container>
|
||||||
</footer>
|
</footer>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
import Link from "next/link"
|
import Link from "next/link";
|
||||||
|
|
||||||
import { NavLink } from "@/components/NavLink";
|
import { NavLink } from "@/components/NavLink";
|
||||||
import { Container } from "@/components/Container"
|
import { Container } from "@/components/Container";
|
||||||
import { Button } from "@/components/Button"
|
import { Button } from "@/components/Button";
|
||||||
|
|
||||||
export function Header() {
|
export function Header() {
|
||||||
return (
|
return (
|
||||||
|
|
@ -24,7 +24,8 @@ export function Header() {
|
||||||
<div className="flex items-center gap-x-5 md:gap-x-8">
|
<div className="flex items-center gap-x-5 md:gap-x-8">
|
||||||
<Button href="mailto:iam@vojtechmares.com" color="blue">
|
<Button href="mailto:iam@vojtechmares.com" color="blue">
|
||||||
<span className="text-lg">
|
<span className="text-lg">
|
||||||
Napište mi <span className="hidden lg:inline">ještě dnes</span>
|
Napište mi{" "}
|
||||||
|
<span className="hidden lg:inline">ještě dnes</span>
|
||||||
</span>
|
</span>
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
import { Button } from '@/components/Button'
|
import { Button } from "@/components/Button";
|
||||||
import { Container } from '@/components/Container'
|
import { Container } from "@/components/Container";
|
||||||
|
|
||||||
export function Hero() {
|
export function Hero() {
|
||||||
return (
|
return (
|
||||||
<Container className="pt-20 pb-16 text-center lg:pt-32">
|
<Container className="pt-20 pb-16 text-center lg:pt-32">
|
||||||
<h1 className="mx-auto max-w-4xl font-display text-5xl font-medium tracking-tight text-slate-900 sm:text-7xl">
|
<h1 className="font-display mx-auto max-w-4xl text-5xl font-medium tracking-tight text-slate-900 sm:text-7xl">
|
||||||
DevOps{' '}
|
DevOps{" "}
|
||||||
<span className="relative whitespace-nowrap text-blue-600">
|
<span className="relative whitespace-nowrap text-blue-600">
|
||||||
<svg
|
<svg
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
|
|
@ -16,15 +16,17 @@ export function Hero() {
|
||||||
<path d="M203.371.916c-26.013-2.078-76.686 1.963-124.73 9.946L67.3 12.749C35.421 18.062 18.2 21.766 6.004 25.934 1.244 27.561.828 27.778.874 28.61c.07 1.214.828 1.121 9.595-1.176 9.072-2.377 17.15-3.92 39.246-7.496C123.565 7.986 157.869 4.492 195.942 5.046c7.461.108 19.25 1.696 19.17 2.582-.107 1.183-7.874 4.31-25.75 10.366-21.992 7.45-35.43 12.534-36.701 13.884-2.173 2.308-.202 4.407 4.442 4.734 2.654.187 3.263.157 15.593-.78 35.401-2.686 57.944-3.488 88.365-3.143 46.327.526 75.721 2.23 130.788 7.584 19.787 1.924 20.814 1.98 24.557 1.332l.066-.011c1.201-.203 1.53-1.825.399-2.335-2.911-1.31-4.893-1.604-22.048-3.261-57.509-5.556-87.871-7.36-132.059-7.842-23.239-.254-33.617-.116-50.627.674-11.629.54-42.371 2.494-46.696 2.967-2.359.259 8.133-3.625 26.504-9.81 23.239-7.825 27.934-10.149 28.304-14.005.417-4.348-3.529-6-16.878-7.066Z" />
|
<path d="M203.371.916c-26.013-2.078-76.686 1.963-124.73 9.946L67.3 12.749C35.421 18.062 18.2 21.766 6.004 25.934 1.244 27.561.828 27.778.874 28.61c.07 1.214.828 1.121 9.595-1.176 9.072-2.377 17.15-3.92 39.246-7.496C123.565 7.986 157.869 4.492 195.942 5.046c7.461.108 19.25 1.696 19.17 2.582-.107 1.183-7.874 4.31-25.75 10.366-21.992 7.45-35.43 12.534-36.701 13.884-2.173 2.308-.202 4.407 4.442 4.734 2.654.187 3.263.157 15.593-.78 35.401-2.686 57.944-3.488 88.365-3.143 46.327.526 75.721 2.23 130.788 7.584 19.787 1.924 20.814 1.98 24.557 1.332l.066-.011c1.201-.203 1.53-1.825.399-2.335-2.911-1.31-4.893-1.604-22.048-3.261-57.509-5.556-87.871-7.36-132.059-7.842-23.239-.254-33.617-.116-50.627.674-11.629.54-42.371 2.494-46.696 2.967-2.359.259 8.133-3.625 26.504-9.81 23.239-7.825 27.934-10.149 28.304-14.005.417-4.348-3.529-6-16.878-7.066Z" />
|
||||||
</svg>
|
</svg>
|
||||||
<span className="relative">jednoduše</span>
|
<span className="relative">jednoduše</span>
|
||||||
</span>{' '}
|
</span>{" "}
|
||||||
pro všechny.
|
pro všechny.
|
||||||
</h1>
|
</h1>
|
||||||
<p className="mx-auto mt-6 max-w-2xl text-lg tracking-tight text-slate-700">
|
<p className="mx-auto mt-6 max-w-2xl text-lg tracking-tight text-slate-700">
|
||||||
Společně snížíme vaše náklady na infrasturkuturu,
|
Společně snížíme vaše náklady na infrasturkuturu, zbavíme se technického
|
||||||
zbavíme se technického dluhu a připravíme vaší IT infrastrukturu na rapidní růst.
|
dluhu a připravíme vaší IT infrastrukturu na rapidní růst.
|
||||||
</p>
|
</p>
|
||||||
<div className="mt-10 flex justify-center gap-x-6">
|
<div className="mt-10 flex justify-center gap-x-6">
|
||||||
<Button href="https://calendly.com/vojtechmares/30min">Domluvme si schůzku</Button>
|
<Button href="https://calendly.com/vojtechmares/30min">
|
||||||
|
Domluvme si schůzku
|
||||||
|
</Button>
|
||||||
{/* <Button
|
{/* <Button
|
||||||
href="https://www.youtube.com/watch?v=dQw4w9WgXcQ"
|
href="https://www.youtube.com/watch?v=dQw4w9WgXcQ"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
|
|
@ -74,5 +76,5 @@ export function Hero() {
|
||||||
</ul>
|
</ul>
|
||||||
</div> */}
|
</div> */}
|
||||||
</Container>
|
</Container>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
import { ReactNode } from 'react';
|
import { ReactNode } from "react";
|
||||||
import Link from 'next/link';
|
import Link from "next/link";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
href: string,
|
href: string;
|
||||||
children: ReactNode,
|
children: ReactNode;
|
||||||
}
|
};
|
||||||
|
|
||||||
export function NavLink({ href, children }: Props) {
|
export function NavLink({ href, children }: Props) {
|
||||||
return (
|
return (
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
import clsx from 'clsx'
|
import clsx from "clsx";
|
||||||
|
|
||||||
import { Button } from '@/components/Button'
|
import { Button } from "@/components/Button";
|
||||||
import { Container } from '@/components/Container'
|
import { Container } from "@/components/Container";
|
||||||
|
|
||||||
type SwirlyDoodleProps = { className: string }
|
type SwirlyDoodleProps = { className: string };
|
||||||
|
|
||||||
function SwirlyDoodle({ className }: SwirlyDoodleProps) {
|
function SwirlyDoodle({ className }: SwirlyDoodleProps) {
|
||||||
return (
|
return (
|
||||||
|
|
@ -19,41 +19,48 @@ function SwirlyDoodle({ className }: SwirlyDoodleProps) {
|
||||||
d="M240.172 22.994c-8.007 1.246-15.477 2.23-31.26 4.114-18.506 2.21-26.323 2.977-34.487 3.386-2.971.149-3.727.324-6.566 1.523-15.124 6.388-43.775 9.404-69.425 7.31-26.207-2.14-50.986-7.103-78-15.624C10.912 20.7.988 16.143.734 14.657c-.066-.381.043-.344 1.324.456 10.423 6.506 49.649 16.322 77.8 19.468 23.708 2.65 38.249 2.95 55.821 1.156 9.407-.962 24.451-3.773 25.101-4.692.074-.104.053-.155-.058-.135-1.062.195-13.863-.271-18.848-.687-16.681-1.389-28.722-4.345-38.142-9.364-15.294-8.15-7.298-19.232 14.802-20.514 16.095-.934 32.793 1.517 47.423 6.96 13.524 5.033 17.942 12.326 11.463 18.922l-.859.874.697-.006c2.681-.026 15.304-1.302 29.208-2.953 25.845-3.07 35.659-4.519 54.027-7.978 9.863-1.858 11.021-2.048 13.055-2.145a61.901 61.901 0 0 0 4.506-.417c1.891-.259 2.151-.267 1.543-.047-.402.145-2.33.913-4.285 1.707-4.635 1.882-5.202 2.07-8.736 2.903-3.414.805-19.773 3.797-26.404 4.829Zm40.321-9.93c.1-.066.231-.085.29-.041.059.043-.024.096-.183.119-.177.024-.219-.007-.107-.079ZM172.299 26.22c9.364-6.058 5.161-12.039-12.304-17.51-11.656-3.653-23.145-5.47-35.243-5.576-22.552-.198-33.577 7.462-21.321 14.814 12.012 7.205 32.994 10.557 61.531 9.831 4.563-.116 5.372-.288 7.337-1.559Z"
|
d="M240.172 22.994c-8.007 1.246-15.477 2.23-31.26 4.114-18.506 2.21-26.323 2.977-34.487 3.386-2.971.149-3.727.324-6.566 1.523-15.124 6.388-43.775 9.404-69.425 7.31-26.207-2.14-50.986-7.103-78-15.624C10.912 20.7.988 16.143.734 14.657c-.066-.381.043-.344 1.324.456 10.423 6.506 49.649 16.322 77.8 19.468 23.708 2.65 38.249 2.95 55.821 1.156 9.407-.962 24.451-3.773 25.101-4.692.074-.104.053-.155-.058-.135-1.062.195-13.863-.271-18.848-.687-16.681-1.389-28.722-4.345-38.142-9.364-15.294-8.15-7.298-19.232 14.802-20.514 16.095-.934 32.793 1.517 47.423 6.96 13.524 5.033 17.942 12.326 11.463 18.922l-.859.874.697-.006c2.681-.026 15.304-1.302 29.208-2.953 25.845-3.07 35.659-4.519 54.027-7.978 9.863-1.858 11.021-2.048 13.055-2.145a61.901 61.901 0 0 0 4.506-.417c1.891-.259 2.151-.267 1.543-.047-.402.145-2.33.913-4.285 1.707-4.635 1.882-5.202 2.07-8.736 2.903-3.414.805-19.773 3.797-26.404 4.829Zm40.321-9.93c.1-.066.231-.085.29-.041.059.043-.024.096-.183.119-.177.024-.219-.007-.107-.079ZM172.299 26.22c9.364-6.058 5.161-12.039-12.304-17.51-11.656-3.653-23.145-5.47-35.243-5.576-22.552-.198-33.577 7.462-21.321 14.814 12.012 7.205 32.994 10.557 61.531 9.831 4.563-.116 5.372-.288 7.337-1.559Z"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
type PlanProps = {
|
type PlanProps = {
|
||||||
name: string,
|
name: string;
|
||||||
price: string,
|
price: string;
|
||||||
description: string,
|
description: string;
|
||||||
href: string,
|
href: string;
|
||||||
featured?: boolean,
|
featured?: boolean;
|
||||||
buttonText?: string
|
buttonText?: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
function Plan({ name, price, description, href, featured = false, buttonText = 'Napište mi' }: PlanProps) {
|
function Plan({
|
||||||
|
name,
|
||||||
|
price,
|
||||||
|
description,
|
||||||
|
href,
|
||||||
|
featured = false,
|
||||||
|
buttonText = "Napište mi",
|
||||||
|
}: PlanProps) {
|
||||||
return (
|
return (
|
||||||
<section
|
<section
|
||||||
className={clsx(
|
className={clsx(
|
||||||
'flex flex-col rounded-3xl px-6 sm:px-8',
|
"flex flex-col rounded-3xl px-6 sm:px-8",
|
||||||
featured ? 'order-first bg-blue-600 py-8 lg:order-none' : 'lg:py-8'
|
featured ? "order-first bg-blue-600 py-8 lg:order-none" : "lg:py-8"
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<h3 className="mt-5 font-display text-4xl text-white">{name}</h3>
|
<h3 className="font-display mt-5 text-4xl text-white">{name}</h3>
|
||||||
<p className="mt-4 font-display text-lg font-light tracking-tight text-white">
|
<p className="font-display mt-4 text-lg font-light tracking-tight text-white">
|
||||||
{price}
|
{price}
|
||||||
</p>
|
</p>
|
||||||
<p
|
<p
|
||||||
className={clsx(
|
className={clsx(
|
||||||
'mt-2 text-base',
|
"mt-2 text-base",
|
||||||
featured ? 'text-white' : 'text-slate-400'
|
featured ? "text-white" : "text-slate-400"
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{description}
|
{description}
|
||||||
</p>
|
</p>
|
||||||
<Button
|
<Button
|
||||||
href={href}
|
href={href}
|
||||||
variant={featured ? 'solid' : 'outline'}
|
variant={featured ? "solid" : "outline"}
|
||||||
color="white"
|
color="white"
|
||||||
className="mt-16"
|
className="mt-16"
|
||||||
aria-label={`Get started with the ${name} plan for ${price}`}
|
aria-label={`Get started with the ${name} plan for ${price}`}
|
||||||
|
|
@ -61,7 +68,7 @@ function Plan({ name, price, description, href, featured = false, buttonText = '
|
||||||
{buttonText}
|
{buttonText}
|
||||||
</Button>
|
</Button>
|
||||||
</section>
|
</section>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Pricing() {
|
export function Pricing() {
|
||||||
|
|
@ -77,11 +84,12 @@ export function Pricing() {
|
||||||
<span className="relative whitespace-nowrap">
|
<span className="relative whitespace-nowrap">
|
||||||
<SwirlyDoodle className="absolute top-1/2 left-0 h-[1em] w-full fill-blue-400" />
|
<SwirlyDoodle className="absolute top-1/2 left-0 h-[1em] w-full fill-blue-400" />
|
||||||
<span className="relative">Co pro Vás,</span>
|
<span className="relative">Co pro Vás,</span>
|
||||||
</span>{' '}
|
</span>{" "}
|
||||||
můžu udělat.
|
můžu udělat.
|
||||||
</h3>
|
</h3>
|
||||||
<p className="mt-4 text-lg text-slate-400">
|
<p className="mt-4 text-lg text-slate-400">
|
||||||
It doesn’t matter what size your company is, we will find a way to help you.
|
It doesn’t matter what size your company is, we will find a way to
|
||||||
|
help you.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="-mx-4 mt-16 grid max-w-2xl grid-cols-1 gap-y-10 sm:mx-auto lg:-mx-8 lg:max-w-none lg:grid-cols-3 xl:mx-0 xl:gap-x-8">
|
<div className="-mx-4 mt-16 grid max-w-2xl grid-cols-1 gap-y-10 sm:mx-auto lg:-mx-8 lg:max-w-none lg:grid-cols-3 xl:mx-0 xl:gap-x-8">
|
||||||
|
|
@ -109,5 +117,5 @@ export function Pricing() {
|
||||||
</div>
|
</div>
|
||||||
</Container>
|
</Container>
|
||||||
</section>
|
</section>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,27 +1,31 @@
|
||||||
import Image from 'next/image'
|
import Image from "next/image";
|
||||||
import clsx from 'clsx'
|
import clsx from "clsx";
|
||||||
|
|
||||||
import { Container } from '@/components/Container'
|
import { Container } from "@/components/Container";
|
||||||
import backgroundImage from '@/images/background-features.jpg'
|
import backgroundImage from "@/images/background-features.jpg";
|
||||||
|
|
||||||
const steps = [
|
const steps = [
|
||||||
{
|
{
|
||||||
name: 'Analýza současného stavu',
|
name: "Analýza současného stavu",
|
||||||
description: 'Zjistíme kde jsou slabá místa vaší infrastruktury nebo aplikace, a nebo obojího.',
|
description:
|
||||||
|
"Zjistíme kde jsou slabá místa vaší infrastruktury nebo aplikace, a nebo obojího.",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Návrh řešení',
|
name: "Návrh řešení",
|
||||||
description: 'Navrhnu, jak tato slabá místa odstranit, na co si dát pozor a naplánujeme případné další kroky. ',
|
description:
|
||||||
|
"Navrhnu, jak tato slabá místa odstranit, na co si dát pozor a naplánujeme případné další kroky. ",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Implementace',
|
name: "Implementace",
|
||||||
description: 'Přesunu vaši aplikaci do Kubernetes, ať na vašem vlastním hardware nebo v public cloudu. Celá infrastruktura bude jasně deklarovaná jako kód pomocí Terraformu.',
|
description:
|
||||||
|
"Přesunu vaši aplikaci do Kubernetes, ať na vašem vlastním hardware nebo v public cloudu. Celá infrastruktura bude jasně deklarovaná jako kód pomocí Terraformu.",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Proškolení vašeho týmu',
|
name: "Proškolení vašeho týmu",
|
||||||
description: 'Naučím váš tým používat moderní technologie, tak abyste mohli rozvíjet vaší aplikaci a byznys a technologie byly nástrojem k rozvoji, ne břemenem, které s sebou táhnete.',
|
description:
|
||||||
|
"Naučím váš tým používat moderní technologie, tak abyste mohli rozvíjet vaší aplikaci a byznys a technologie byly nástrojem k rozvoji, ne břemenem, které s sebou táhnete.",
|
||||||
},
|
},
|
||||||
]
|
];
|
||||||
|
|
||||||
export function PrimaryFeatures() {
|
export function PrimaryFeatures() {
|
||||||
return (
|
return (
|
||||||
|
|
@ -48,11 +52,23 @@ export function PrimaryFeatures() {
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<nav aria-label="Progress">
|
<nav aria-label="Progress">
|
||||||
<ol role="list" className="overflow-hidden pt-2 mt-20 max-w-3xl mx-auto">
|
<ol
|
||||||
|
role="list"
|
||||||
|
className="mx-auto mt-20 max-w-3xl overflow-hidden pt-2"
|
||||||
|
>
|
||||||
{steps.map((step, stepIdx) => (
|
{steps.map((step, stepIdx) => (
|
||||||
<li key={step.name} className={clsx(stepIdx !== steps.length - 1 ? 'pb-10' : '', 'relative')}>
|
<li
|
||||||
|
key={step.name}
|
||||||
|
className={clsx(
|
||||||
|
stepIdx !== steps.length - 1 ? "pb-10" : "",
|
||||||
|
"relative"
|
||||||
|
)}
|
||||||
|
>
|
||||||
{stepIdx !== steps.length - 1 ? (
|
{stepIdx !== steps.length - 1 ? (
|
||||||
<div className="absolute top-4 left-6 -ml-px mt-0.5 h-full w-0.5 bg-white" aria-hidden="true" />
|
<div
|
||||||
|
className="absolute top-4 left-6 -ml-px mt-0.5 h-full w-0.5 bg-white"
|
||||||
|
aria-hidden="true"
|
||||||
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
<div className="group relative flex items-start">
|
<div className="group relative flex items-start">
|
||||||
<span className="flex h-9 items-center" aria-hidden="true">
|
<span className="flex h-9 items-center" aria-hidden="true">
|
||||||
|
|
@ -61,8 +77,12 @@ export function PrimaryFeatures() {
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
<span className="ml-4 flex min-w-0 flex-col">
|
<span className="ml-4 flex min-w-0 flex-col">
|
||||||
<span className="text-2xl text-white font-normal">{step.name}</span>
|
<span className="text-2xl font-normal text-white">
|
||||||
<span className="text-lg text-blue-200">{step.description}</span>
|
{step.name}
|
||||||
|
</span>
|
||||||
|
<span className="text-lg text-blue-200">
|
||||||
|
{step.description}
|
||||||
|
</span>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
|
|
@ -71,5 +91,5 @@ export function PrimaryFeatures() {
|
||||||
</nav>
|
</nav>
|
||||||
</Container>
|
</Container>
|
||||||
</section>
|
</section>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,122 +1,125 @@
|
||||||
import { ReactNode } from 'react'
|
import { ReactNode } from "react";
|
||||||
import Image, { StaticImageData } from 'next/image'
|
import Image, { StaticImageData } from "next/image";
|
||||||
import { Tab } from '@headlessui/react'
|
import { Tab } from "@headlessui/react";
|
||||||
import clsx from 'clsx'
|
import clsx from "clsx";
|
||||||
|
|
||||||
import { Container } from '@/components/Container'
|
import { Container } from "@/components/Container";
|
||||||
|
|
||||||
|
import screenshotArgoCD from "@/images/screenshots/argocd.png";
|
||||||
|
import screenshotGitLab from "@/images/screenshots/gitlab.png";
|
||||||
|
import screenshotTerraform from "@/images/screenshots/terraform.png";
|
||||||
|
import screenshotKubernetes from "@/images/screenshots/kubernetes.png";
|
||||||
|
import screenshotGrafana from "@/images/screenshots/grafana.png";
|
||||||
|
import screenshotPrometheus from "@/images/screenshots/prometheus.png";
|
||||||
|
|
||||||
import screenshotArgoCD from '@/images/screenshots/argocd.png'
|
import logoTerraform from "@/images/logos/tools/terraform.svg";
|
||||||
import screenshotGitLab from '@/images/screenshots/gitlab.png'
|
import logoGit from "@/images/logos/tools/git.svg";
|
||||||
import screenshotTerraform from '@/images/screenshots/terraform.png'
|
import logoKubernetes from "@/images/logos/tools/kubernetes.svg";
|
||||||
import screenshotKubernetes from '@/images/screenshots/kubernetes.png'
|
import logoPrometheus from "@/images/logos/tools/prometheus.svg";
|
||||||
import screenshotGrafana from '@/images/screenshots/grafana.png'
|
import logoArgo from "@/images/logos/tools/argo.svg";
|
||||||
import screenshotPrometheus from '@/images/screenshots/prometheus.png'
|
import logoGrafana from "@/images/logos/tools/grafana.svg";
|
||||||
|
|
||||||
import logoTerraform from '@/images/logos/tools/terraform.svg'
|
|
||||||
import logoGit from '@/images/logos/tools/git.svg'
|
|
||||||
import logoKubernetes from '@/images/logos/tools/kubernetes.svg'
|
|
||||||
import logoPrometheus from '@/images/logos/tools/prometheus.svg'
|
|
||||||
import logoArgo from '@/images/logos/tools/argo.svg'
|
|
||||||
import logoGrafana from '@/images/logos/tools/grafana.svg'
|
|
||||||
|
|
||||||
|
|
||||||
const features = [
|
const features = [
|
||||||
{
|
{
|
||||||
name: 'Verzování',
|
name: "Verzování",
|
||||||
summary:
|
summary: "Spravujte svůj kód pomocí verzovacího nástroje Git.",
|
||||||
'Spravujte svůj kód pomocí verzovacího nástroje Git.',
|
|
||||||
description:
|
description:
|
||||||
'Mějte historii změn v aplikaci od začátku až do dnes, můžete se kdykoliv vrátit do bodu v čase. Řešte konflikty včas, předtím než je nasadíte do produkce a umožněte vaším programátorům spolupracovat na jednou, aniž by si překáželi.',
|
"Mějte historii změn v aplikaci od začátku až do dnes, můžete se kdykoliv vrátit do bodu v čase. Řešte konflikty včas, předtím než je nasadíte do produkce a umožněte vaším programátorům spolupracovat na jednou, aniž by si překáželi.",
|
||||||
image: screenshotGitLab,
|
image: screenshotGitLab,
|
||||||
icon: logoGit,
|
icon: logoGit,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Infastruktura jako kód',
|
name: "Infastruktura jako kód",
|
||||||
summary: 'Mějte vaši infrastrukturu deklarativně definovanou a verzovanou, díky Terraformu.',
|
summary:
|
||||||
|
"Mějte vaši infrastrukturu deklarativně definovanou a verzovanou, díky Terraformu.",
|
||||||
description:
|
description:
|
||||||
'Vaši infrastrukturu můžete snadno přesunout k jinému poskytovateli, a nebo jen vytvořit nové prostředí pro zákazníka, aby si váš produkt vyzkoušel a to během minut.',
|
"Vaši infrastrukturu můžete snadno přesunout k jinému poskytovateli, a nebo jen vytvořit nové prostředí pro zákazníka, aby si váš produkt vyzkoušel a to během minut.",
|
||||||
image: screenshotTerraform,
|
image: screenshotTerraform,
|
||||||
icon: logoTerraform,
|
icon: logoTerraform,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Orchestrace',
|
name: "Orchestrace",
|
||||||
summary:
|
summary:
|
||||||
'Nechte vaši aplikaci běžet napříč mnoha servery a škálovat dle potřeb.',
|
"Nechte vaši aplikaci běžet napříč mnoha servery a škálovat dle potřeb.",
|
||||||
description:
|
description:
|
||||||
'Kubernetes se stalo nejen standardem, ale i hlavní platformou pro vývoj aplikací ať SaaS nebo dodávaných třetím stranám. Jde o skvělou platformu pro provoz vaší aplikace ať ve veřejném cloudu nebo na vlastním hardwaru popř. on edge blízko koncových uživatelů.',
|
"Kubernetes se stalo nejen standardem, ale i hlavní platformou pro vývoj aplikací ať SaaS nebo dodávaných třetím stranám. Jde o skvělou platformu pro provoz vaší aplikace ať ve veřejném cloudu nebo na vlastním hardwaru popř. on edge blízko koncových uživatelů.",
|
||||||
image: screenshotKubernetes,
|
image: screenshotKubernetes,
|
||||||
icon: logoKubernetes,
|
icon: logoKubernetes,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Monitoring',
|
name: "Monitoring",
|
||||||
summary:
|
summary: "Sledujte Vaši aplikaci, jak se chová v čase.",
|
||||||
'Sledujte Vaši aplikaci, jak se chová v čase.',
|
|
||||||
description:
|
description:
|
||||||
'Prometheus je standard pro monitoring aplikací, ať v Kubernetes ale i mimo. Sbírejte telemetrická data v čase o vaši aplikaci. Vyhodnoťte, kde má vaše aplikace slabá místa. Zároveň můžete tvořit pravidla, dle kterých vás AlertManager upozorní, když se něco pokazí.',
|
"Prometheus je standard pro monitoring aplikací, ať v Kubernetes ale i mimo. Sbírejte telemetrická data v čase o vaši aplikaci. Vyhodnoťte, kde má vaše aplikace slabá místa. Zároveň můžete tvořit pravidla, dle kterých vás AlertManager upozorní, když se něco pokazí.",
|
||||||
image: screenshotPrometheus,
|
image: screenshotPrometheus,
|
||||||
icon: logoPrometheus,
|
icon: logoPrometheus,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Vizualizace',
|
name: "Vizualizace",
|
||||||
summary:
|
summary:
|
||||||
'Od grafů zatížení procesoru po počet otevřených TCP spojení, vše jasně a přehledně.',
|
"Od grafů zatížení procesoru po počet otevřených TCP spojení, vše jasně a přehledně.",
|
||||||
description:
|
description:
|
||||||
'Grafana je skvělý nástroj pro vizualizaci dat z monitoringu, vytvořte si dashboardy pro jednotlivé části vaší aplikace, mějte pohromadě infrastrukturu, provoz na síti, dostupnost a třeba počet neúspěšných pokusů o přihlášení, zda vůči vaší aplikaci neprobíhá hackerský útok.',
|
"Grafana je skvělý nástroj pro vizualizaci dat z monitoringu, vytvořte si dashboardy pro jednotlivé části vaší aplikace, mějte pohromadě infrastrukturu, provoz na síti, dostupnost a třeba počet neúspěšných pokusů o přihlášení, zda vůči vaší aplikaci neprobíhá hackerský útok.",
|
||||||
image: screenshotGrafana,
|
image: screenshotGrafana,
|
||||||
icon: logoGrafana,
|
icon: logoGrafana,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'GitOps',
|
name: "GitOps",
|
||||||
summary:
|
summary:
|
||||||
'Spravujte stav Vašich prostředí deklarativně, ať vždy víte, jaký je aktuální stav.',
|
"Spravujte stav Vašich prostředí deklarativně, ať vždy víte, jaký je aktuální stav.",
|
||||||
description:
|
description:
|
||||||
'ArgoCD je spolehlivý nástroj pro práci s Kubernetes a nasazováním změn a přitom si udržovat přehled o právě nasazených aplikacích, verzím a konfiguraci, snadno, soplehlivě, verzovaně.',
|
"ArgoCD je spolehlivý nástroj pro práci s Kubernetes a nasazováním změn a přitom si udržovat přehled o právě nasazených aplikacích, verzím a konfiguraci, snadno, soplehlivě, verzovaně.",
|
||||||
image: screenshotArgoCD,
|
image: screenshotArgoCD,
|
||||||
icon: logoArgo,
|
icon: logoArgo,
|
||||||
},
|
},
|
||||||
]
|
];
|
||||||
|
|
||||||
type FeatureType = {
|
type FeatureType = {
|
||||||
name: string|ReactNode,
|
name: string | ReactNode;
|
||||||
summary: string,
|
summary: string;
|
||||||
description: string,
|
description: string;
|
||||||
image: StaticImageData,
|
image: StaticImageData;
|
||||||
icon: any,
|
icon: any;
|
||||||
}
|
};
|
||||||
|
|
||||||
type FeatureProps = {
|
type FeatureProps = {
|
||||||
feature: FeatureType,
|
feature: FeatureType;
|
||||||
isActive: boolean,
|
isActive: boolean;
|
||||||
className?: string,
|
className?: string;
|
||||||
props?: any,
|
props?: any;
|
||||||
}
|
};
|
||||||
|
|
||||||
function Feature({ feature, isActive, className, ...props }: FeatureProps) {
|
function Feature({ feature, isActive, className, ...props }: FeatureProps) {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={clsx(className, !isActive && 'opacity-75 hover:opacity-100')}
|
className={clsx(className, !isActive && "opacity-75 hover:opacity-100")}
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
<Image src={feature.icon} width="128" height="128" className={clsx(
|
<Image
|
||||||
|
src={feature.icon}
|
||||||
|
width="128"
|
||||||
|
height="128"
|
||||||
|
className={clsx(
|
||||||
// 'w-32',
|
// 'w-32',
|
||||||
'rounded-lg p-2 flex justify-center',
|
"flex justify-center rounded-lg p-2"
|
||||||
// isActive ? 'fill-blue-600' : 'fill-slate-500'
|
// isActive ? 'fill-blue-600' : 'fill-slate-500'
|
||||||
)} alt="" />
|
)}
|
||||||
|
alt=""
|
||||||
|
/>
|
||||||
<h3
|
<h3
|
||||||
className={clsx(
|
className={clsx(
|
||||||
'mt-6 text-lg font-medium',
|
"mt-6 text-lg font-medium",
|
||||||
isActive ? 'text-blue-600' : 'text-slate-600'
|
isActive ? "text-blue-600" : "text-slate-600"
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{feature.name}
|
{feature.name}
|
||||||
</h3>
|
</h3>
|
||||||
<p className="mt-2 font-display text-xl text-slate-900">
|
<p className="font-display mt-2 text-xl text-slate-900">
|
||||||
{feature.summary}
|
{feature.summary}
|
||||||
</p>
|
</p>
|
||||||
<p className="mt-4 text-sm text-slate-600">{feature.description}</p>
|
<p className="mt-4 text-sm text-slate-600">{feature.description}</p>
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function FeaturesMobile() {
|
function FeaturesMobile() {
|
||||||
|
|
@ -141,7 +144,7 @@ function FeaturesMobile() {
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function FeaturesDesktop() {
|
function FeaturesDesktop() {
|
||||||
|
|
@ -167,15 +170,15 @@ function FeaturesDesktop() {
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</Tab.List>
|
</Tab.List>
|
||||||
<Tab.Panels className="relative mt-20 overflow-hidden rounded-4xl bg-slate-200 px-14 py-16 xl:px-16">
|
<Tab.Panels className="rounded-4xl relative mt-20 overflow-hidden bg-slate-200 px-14 py-16 xl:px-16">
|
||||||
<div className="-mx-5 flex">
|
<div className="-mx-5 flex">
|
||||||
{features.map((feature, featureIndex) => (
|
{features.map((feature, featureIndex) => (
|
||||||
<Tab.Panel
|
<Tab.Panel
|
||||||
static
|
static
|
||||||
key={feature.name}
|
key={feature.name}
|
||||||
className={clsx(
|
className={clsx(
|
||||||
'px-5 transition duration-500 ease-in-out [&:not(:focus-visible)]:focus:outline-none',
|
"px-5 transition duration-500 ease-in-out [&:not(:focus-visible)]:focus:outline-none",
|
||||||
featureIndex !== selectedIndex && 'opacity-60'
|
featureIndex !== selectedIndex && "opacity-60"
|
||||||
)}
|
)}
|
||||||
style={{ transform: `translateX(-${selectedIndex * 100}%)` }}
|
style={{ transform: `translateX(-${selectedIndex * 100}%)` }}
|
||||||
aria-hidden={featureIndex !== selectedIndex}
|
aria-hidden={featureIndex !== selectedIndex}
|
||||||
|
|
@ -193,12 +196,12 @@ function FeaturesDesktop() {
|
||||||
</Tab.Panel>
|
</Tab.Panel>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
<div className="pointer-events-none absolute inset-0 rounded-4xl ring-1 ring-inset ring-slate-900/10" />
|
<div className="rounded-4xl pointer-events-none absolute inset-0 ring-1 ring-inset ring-slate-900/10" />
|
||||||
</Tab.Panels>
|
</Tab.Panels>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</Tab.Group>
|
</Tab.Group>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function TechStack() {
|
export function TechStack() {
|
||||||
|
|
@ -214,13 +217,13 @@ export function TechStack() {
|
||||||
Open Source DevOps stack
|
Open Source DevOps stack
|
||||||
</h2>
|
</h2>
|
||||||
<p className="mt-4 text-lg tracking-tight text-slate-700">
|
<p className="mt-4 text-lg tracking-tight text-slate-700">
|
||||||
Věřím v Open Source technologie, prakticky všichni je denně využíváme
|
Věřím v Open Source technologie, prakticky všichni je denně
|
||||||
a jsou naší budoucností.
|
využíváme a jsou naší budoucností.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<FeaturesMobile />
|
<FeaturesMobile />
|
||||||
<FeaturesDesktop />
|
<FeaturesDesktop />
|
||||||
</Container>
|
</Container>
|
||||||
</section>
|
</section>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,29 +1,29 @@
|
||||||
import Image from 'next/image'
|
import Image from "next/image";
|
||||||
|
|
||||||
import { Container } from '@/components/Container'
|
import { Container } from "@/components/Container";
|
||||||
import avatarImage1 from '@/images/avatars/avatar-1.png'
|
import avatarImage1 from "@/images/avatars/avatar-1.png";
|
||||||
import avatarImage2 from '@/images/avatars/avatar-2.png'
|
import avatarImage2 from "@/images/avatars/avatar-2.png";
|
||||||
import avatarImage3 from '@/images/avatars/avatar-3.png'
|
import avatarImage3 from "@/images/avatars/avatar-3.png";
|
||||||
import avatarImage4 from '@/images/avatars/avatar-4.png'
|
import avatarImage4 from "@/images/avatars/avatar-4.png";
|
||||||
import avatarImage5 from '@/images/avatars/avatar-5.png'
|
import avatarImage5 from "@/images/avatars/avatar-5.png";
|
||||||
|
|
||||||
const testimonials = [
|
const testimonials = [
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
content:
|
content:
|
||||||
'TaxPal is so easy to use I can’t help but wonder if it’s really doing the things the government expects me to do.',
|
"TaxPal is so easy to use I can’t help but wonder if it’s really doing the things the government expects me to do.",
|
||||||
author: {
|
author: {
|
||||||
name: 'Sheryl Berge',
|
name: "Sheryl Berge",
|
||||||
role: 'CEO at Lynch LLC',
|
role: "CEO at Lynch LLC",
|
||||||
image: avatarImage1,
|
image: avatarImage1,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
content:
|
content:
|
||||||
'I’m trying to get a hold of someone in support, I’m in a lot of trouble right now and they are saying it has something to do with my books. Please get back to me right away.',
|
"I’m trying to get a hold of someone in support, I’m in a lot of trouble right now and they are saying it has something to do with my books. Please get back to me right away.",
|
||||||
author: {
|
author: {
|
||||||
name: 'Amy Hahn',
|
name: "Amy Hahn",
|
||||||
role: 'Director at Velocity Industries',
|
role: "Director at Velocity Industries",
|
||||||
image: avatarImage4,
|
image: avatarImage4,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -31,19 +31,19 @@ const testimonials = [
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
content:
|
content:
|
||||||
'The best part about TaxPal is every time I pay my employees, my bank balance doesn’t go down like it used to. Looking forward to spending this extra cash when I figure out why my card is being declined.',
|
"The best part about TaxPal is every time I pay my employees, my bank balance doesn’t go down like it used to. Looking forward to spending this extra cash when I figure out why my card is being declined.",
|
||||||
author: {
|
author: {
|
||||||
name: 'Leland Kiehn',
|
name: "Leland Kiehn",
|
||||||
role: 'Founder of Kiehn and Sons',
|
role: "Founder of Kiehn and Sons",
|
||||||
image: avatarImage5,
|
image: avatarImage5,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
content:
|
content:
|
||||||
'There are so many things I had to do with my old software that I just don’t do at all with TaxPal. Suspicious but I can’t say I don’t love it.',
|
"There are so many things I had to do with my old software that I just don’t do at all with TaxPal. Suspicious but I can’t say I don’t love it.",
|
||||||
author: {
|
author: {
|
||||||
name: 'Erin Powlowski',
|
name: "Erin Powlowski",
|
||||||
role: 'COO at Armstrong Inc',
|
role: "COO at Armstrong Inc",
|
||||||
image: avatarImage2,
|
image: avatarImage2,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -51,31 +51,31 @@ const testimonials = [
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
content:
|
content:
|
||||||
'I used to have to remit tax to the EU and with TaxPal I somehow don’t have to do that anymore. Nervous to travel there now though.',
|
"I used to have to remit tax to the EU and with TaxPal I somehow don’t have to do that anymore. Nervous to travel there now though.",
|
||||||
author: {
|
author: {
|
||||||
name: 'Peter Renolds',
|
name: "Peter Renolds",
|
||||||
role: 'Founder of West Inc',
|
role: "Founder of West Inc",
|
||||||
image: avatarImage3,
|
image: avatarImage3,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
content:
|
content:
|
||||||
'This is the fourth email I’ve sent to your support team. I am literally being held in jail for tax fraud. Please answer your damn emails, this is important.',
|
"This is the fourth email I’ve sent to your support team. I am literally being held in jail for tax fraud. Please answer your damn emails, this is important.",
|
||||||
author: {
|
author: {
|
||||||
name: 'Amy Hahn',
|
name: "Amy Hahn",
|
||||||
role: 'Director at Velocity Industries',
|
role: "Director at Velocity Industries",
|
||||||
image: avatarImage4,
|
image: avatarImage4,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
]
|
];
|
||||||
|
|
||||||
function QuoteIcon(props: any) {
|
function QuoteIcon(props: any) {
|
||||||
return (
|
return (
|
||||||
<svg aria-hidden="true" width={105} height={78} {...props}>
|
<svg aria-hidden="true" width={105} height={78} {...props}>
|
||||||
<path d="M25.086 77.292c-4.821 0-9.115-1.205-12.882-3.616-3.767-2.561-6.78-6.102-9.04-10.622C1.054 58.534 0 53.411 0 47.686c0-5.273.904-10.396 2.712-15.368 1.959-4.972 4.746-9.567 8.362-13.786a59.042 59.042 0 0 1 12.43-11.3C28.325 3.917 33.599 1.507 39.324 0l11.074 13.786c-6.479 2.561-11.677 5.951-15.594 10.17-3.767 4.219-5.65 7.835-5.65 10.848 0 1.356.377 2.863 1.13 4.52.904 1.507 2.637 3.089 5.198 4.746 3.767 2.41 6.328 4.972 7.684 7.684 1.507 2.561 2.26 5.5 2.26 8.814 0 5.123-1.959 9.19-5.876 12.204-3.767 3.013-8.588 4.52-14.464 4.52Zm54.24 0c-4.821 0-9.115-1.205-12.882-3.616-3.767-2.561-6.78-6.102-9.04-10.622-2.11-4.52-3.164-9.643-3.164-15.368 0-5.273.904-10.396 2.712-15.368 1.959-4.972 4.746-9.567 8.362-13.786a59.042 59.042 0 0 1 12.43-11.3C82.565 3.917 87.839 1.507 93.564 0l11.074 13.786c-6.479 2.561-11.677 5.951-15.594 10.17-3.767 4.219-5.65 7.835-5.65 10.848 0 1.356.377 2.863 1.13 4.52.904 1.507 2.637 3.089 5.198 4.746 3.767 2.41 6.328 4.972 7.684 7.684 1.507 2.561 2.26 5.5 2.26 8.814 0 5.123-1.959 9.19-5.876 12.204-3.767 3.013-8.588 4.52-14.464 4.52Z" />
|
<path d="M25.086 77.292c-4.821 0-9.115-1.205-12.882-3.616-3.767-2.561-6.78-6.102-9.04-10.622C1.054 58.534 0 53.411 0 47.686c0-5.273.904-10.396 2.712-15.368 1.959-4.972 4.746-9.567 8.362-13.786a59.042 59.042 0 0 1 12.43-11.3C28.325 3.917 33.599 1.507 39.324 0l11.074 13.786c-6.479 2.561-11.677 5.951-15.594 10.17-3.767 4.219-5.65 7.835-5.65 10.848 0 1.356.377 2.863 1.13 4.52.904 1.507 2.637 3.089 5.198 4.746 3.767 2.41 6.328 4.972 7.684 7.684 1.507 2.561 2.26 5.5 2.26 8.814 0 5.123-1.959 9.19-5.876 12.204-3.767 3.013-8.588 4.52-14.464 4.52Zm54.24 0c-4.821 0-9.115-1.205-12.882-3.616-3.767-2.561-6.78-6.102-9.04-10.622-2.11-4.52-3.164-9.643-3.164-15.368 0-5.273.904-10.396 2.712-15.368 1.959-4.972 4.746-9.567 8.362-13.786a59.042 59.042 0 0 1 12.43-11.3C82.565 3.917 87.839 1.507 93.564 0l11.074 13.786c-6.479 2.561-11.677 5.951-15.594 10.17-3.767 4.219-5.65 7.835-5.65 10.848 0 1.356.377 2.863 1.13 4.52.904 1.507 2.637 3.089 5.198 4.746 3.767 2.41 6.328 4.972 7.684 7.684 1.507 2.561 2.26 5.5 2.26 8.814 0 5.123-1.959 9.19-5.876 12.204-3.767 3.013-8.588 4.52-14.464 4.52Z" />
|
||||||
</svg>
|
</svg>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Testimonials() {
|
export function Testimonials() {
|
||||||
|
|
@ -140,5 +140,5 @@ export function Testimonials() {
|
||||||
</ul>
|
</ul>
|
||||||
</Container>
|
</Container>
|
||||||
</section>
|
</section>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,45 +1,66 @@
|
||||||
import Image from "next/image"
|
import Image from "next/image";
|
||||||
|
|
||||||
import TrainingType from "@/types/training"
|
import TrainingType from "@/types/training";
|
||||||
import { Container } from "./Container"
|
import { Container } from "./Container";
|
||||||
import { Button } from "./Button"
|
import { Button } from "./Button";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
training: TrainingType
|
training: TrainingType;
|
||||||
}
|
};
|
||||||
|
|
||||||
export const Training = ({ training }: Props) => {
|
export const Training = ({ training }: Props) => {
|
||||||
const formatter = new Intl.NumberFormat('cs', { style: 'currency', currency: 'CZK', maximumFractionDigits: 0})
|
const formatter = new Intl.NumberFormat("cs", {
|
||||||
|
style: "currency",
|
||||||
|
currency: "CZK",
|
||||||
|
maximumFractionDigits: 0,
|
||||||
|
});
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="pb-14 sm:pb-20 lg:pb-32">
|
<div className="pb-14 sm:pb-20 lg:pb-32">
|
||||||
<div className="bg-slate-900 pt-16 pb-16">
|
<div className="bg-slate-900 pt-16 pb-16">
|
||||||
<Container className="flex justify-around">
|
<Container className="flex justify-around">
|
||||||
<Image src={training.logo} className="h-40 w-40 rounded-full" width="1500" height="1500" alt="" />
|
<Image
|
||||||
<h2 className="font-display text-4xl tracking-tight text-white sm:text-6xl self-center">
|
src={training.logo}
|
||||||
|
className="h-40 w-40 rounded-full"
|
||||||
|
width="1500"
|
||||||
|
height="1500"
|
||||||
|
alt=""
|
||||||
|
/>
|
||||||
|
<h2 className="font-display self-center text-4xl tracking-tight text-white sm:text-6xl">
|
||||||
Školení {training.name}
|
Školení {training.name}
|
||||||
</h2>
|
</h2>
|
||||||
</Container>
|
</Container>
|
||||||
</div>
|
</div>
|
||||||
<Container>
|
<Container>
|
||||||
<div className="md:grid md:grid-cols-5 md:gap-x-4 md:gap-y-4 mt-12">
|
<div className="mt-12 md:grid md:grid-cols-5 md:gap-x-4 md:gap-y-4">
|
||||||
<div className="md:col-span-3">
|
<div className="md:col-span-3">
|
||||||
<div className="prose prose-slate prose-h1:text-2xl prose-h2:text-xl prose-h3:text-lg prose-h1:font-medium prose-h2:font-medium prose-h3:font-medium prose-li:my-0" dangerouslySetInnerHTML={{ __html: training.content }} />
|
<div
|
||||||
|
className="prose prose-slate prose-h1:text-2xl prose-h1:font-medium prose-h2:text-xl prose-h2:font-medium prose-h3:text-lg prose-h3:font-medium prose-li:my-0"
|
||||||
|
dangerouslySetInnerHTML={{ __html: training.content }}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="md:col-span-2 mt-8 md:mt-0">
|
<div className="mt-8 md:col-span-2 md:mt-0">
|
||||||
{training.days === 2 ? (
|
{training.days === 2 ? (
|
||||||
<div className=" bg-blue-50 p-4 rounded-lg shadow mb-8">
|
<div className=" mb-8 rounded-lg bg-blue-50 p-4 shadow">
|
||||||
<div className="flex">
|
<div className="flex">
|
||||||
<div className="flex-shrink-0">
|
<div className="flex-shrink-0">
|
||||||
{/* <ExclamationTriangleIcon className="h-5 w-5 text-yellow-400" aria-hidden="true" /> */}
|
{/* <ExclamationTriangleIcon className="h-5 w-5 text-yellow-400" aria-hidden="true" /> */}
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" className="w-5 h-5 text-blue-400">
|
<svg
|
||||||
<path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a.75.75 0 000 1.5h.253a.25.25 0 01.244.304l-.459 2.066A1.75 1.75 0 0010.747 15H11a.75.75 0 000-1.5h-.253a.25.25 0 01-.244-.304l.459-2.066A1.75 1.75 0 009.253 9H9z" clip-rule="evenodd" />
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
fill="currentColor"
|
||||||
|
className="h-5 w-5 text-blue-400"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fill-rule="evenodd"
|
||||||
|
d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a.75.75 0 000 1.5h.253a.25.25 0 01.244.304l-.459 2.066A1.75 1.75 0 0010.747 15H11a.75.75 0 000-1.5h-.253a.25.25 0 01-.244-.304l.459-2.066A1.75 1.75 0 009.253 9H9z"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div className="ml-3">
|
<div className="ml-3">
|
||||||
<p className="text-blue-700">
|
<p className="text-blue-700">
|
||||||
Toto školení je{' '}
|
Toto školení je{" "}
|
||||||
<span className="font-medium text-blue-700">
|
<span className="font-medium text-blue-700">
|
||||||
dvoudenní.
|
dvoudenní.
|
||||||
</span>
|
</span>
|
||||||
|
|
@ -47,20 +68,32 @@ export const Training = ({ training }: Props) => {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : <></>}
|
) : (
|
||||||
<div className="overflow-hidden bg-slate-50 shadow rounded-lg mt-8 md:mt-0">
|
<></>
|
||||||
|
)}
|
||||||
|
<div className="mt-8 overflow-hidden rounded-lg bg-slate-50 shadow md:mt-0">
|
||||||
<div className="px-4 py-5 sm:px-6">
|
<div className="px-4 py-5 sm:px-6">
|
||||||
<h3 className="text-lg font-medium leading-6 text-slate-900" >Cena za školení</h3>
|
<h3 className="text-lg font-medium leading-6 text-slate-900">
|
||||||
|
Cena za školení
|
||||||
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
<div className="border-t border-gray-200 px-4 py-5">
|
<div className="border-t border-gray-200 px-4 py-5">
|
||||||
<dl className="sm:divide-y sm:divide-gray-200">
|
<dl className="sm:divide-y sm:divide-gray-200">
|
||||||
<div className="py-4 sm:grid sm:grid-cols-2 sm:gap-4 sm:py-5 sm:px-6">
|
<div className="py-4 sm:grid sm:grid-cols-2 sm:gap-4 sm:py-5 sm:px-6">
|
||||||
<dt className="font-medium text-gray-500">Veřejný termín</dt>
|
<dt className="font-medium text-gray-500">
|
||||||
<dd className="mt-1 text-gray-900 sm:mt-0">{formatter.format(training.priceOpen)} bez DPH</dd>
|
Veřejný termín
|
||||||
|
</dt>
|
||||||
|
<dd className="mt-1 text-gray-900 sm:mt-0">
|
||||||
|
{formatter.format(training.priceOpen)} bez DPH
|
||||||
|
</dd>
|
||||||
</div>
|
</div>
|
||||||
<div className="py-4 sm:grid sm:grid-cols-2 sm:gap-4 sm:py-5 sm:px-6">
|
<div className="py-4 sm:grid sm:grid-cols-2 sm:gap-4 sm:py-5 sm:px-6">
|
||||||
<dt className="font-medium text-gray-500">Firemní školení</dt>
|
<dt className="font-medium text-gray-500">
|
||||||
<dd className="mt-1 text-gray-900 sm:mt-0">{formatter.format(training.priceCompany)} bez DPH</dd>
|
Firemní školení
|
||||||
|
</dt>
|
||||||
|
<dd className="mt-1 text-gray-900 sm:mt-0">
|
||||||
|
{formatter.format(training.priceCompany)} bez DPH
|
||||||
|
</dd>
|
||||||
</div>
|
</div>
|
||||||
</dl>
|
</dl>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -80,5 +113,5 @@ export const Training = ({ training }: Props) => {
|
||||||
</Container>
|
</Container>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
)
|
);
|
||||||
}
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,95 +1,113 @@
|
||||||
import Image from "next/image"
|
import Image from "next/image";
|
||||||
import clsx from "clsx"
|
import clsx from "clsx";
|
||||||
|
|
||||||
import TrainingType from "@/types/training"
|
import TrainingType from "@/types/training";
|
||||||
import { Container } from "./Container"
|
import { Container } from "./Container";
|
||||||
import { Button } from "./Button"
|
import { Button } from "./Button";
|
||||||
|
|
||||||
type TrainingDetailProps = {
|
type TrainingDetailProps = {
|
||||||
training: TrainingType,
|
training: TrainingType;
|
||||||
className?: string,
|
className?: string;
|
||||||
props?: any[],
|
props?: any[];
|
||||||
}
|
};
|
||||||
|
|
||||||
const TrainingDetail = ({ training, className, ...props }: TrainingDetailProps) => {
|
const TrainingDetail = ({
|
||||||
|
training,
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: TrainingDetailProps) => {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={
|
className={clsx(
|
||||||
clsx(className,
|
className,
|
||||||
"rounded-3xl shadow py-8 px-6 sm:px-8",
|
"rounded-3xl py-8 px-6 shadow sm:px-8",
|
||||||
training.featured ? 'bg-blue-600 py-8' : ''
|
training.featured ? "bg-blue-600 py-8" : ""
|
||||||
)
|
)}
|
||||||
}
|
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
<Image src={training.logo} className="h-32 w-32 rounded-full" width="1500" height="1500" alt="" />
|
<Image
|
||||||
<div className="flex justify-between mt-6">
|
src={training.logo}
|
||||||
|
className="h-32 w-32 rounded-full"
|
||||||
|
width="1500"
|
||||||
|
height="1500"
|
||||||
|
alt=""
|
||||||
|
/>
|
||||||
|
<div className="mt-6 flex justify-between">
|
||||||
<div className="flex">
|
<div className="flex">
|
||||||
<h3
|
<h3 className="font-display text-xl font-medium text-white">
|
||||||
className="text-xl font-display font-medium text-white"
|
|
||||||
>
|
|
||||||
{training.name}
|
{training.name}
|
||||||
</h3>
|
</h3>
|
||||||
{training.new ? (
|
{training.new ? (
|
||||||
<>
|
<>
|
||||||
<span className="ml-2 rounded-full bg-orange-100 px-2 py-1 text-xs font-medium text-orange-800 border border-1 border-orange-800">
|
<span className="border-1 ml-2 rounded-full border border-orange-800 bg-orange-100 px-2 py-1 text-xs font-medium text-orange-800">
|
||||||
new!
|
new!
|
||||||
</span>
|
</span>
|
||||||
</>
|
</>
|
||||||
) : <></> }
|
) : (
|
||||||
|
<></>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
<p className="max-w-2xl text-md text-white">{training.days}{' '}{training.days === 1 ? 'den' : 'dny'}</p>
|
<p className="text-md max-w-2xl text-white">
|
||||||
|
{training.days} {training.days === 1 ? "den" : "dny"}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<p className={
|
<p
|
||||||
clsx(
|
className={clsx(
|
||||||
"mt-4 text-md",
|
"text-md mt-4",
|
||||||
training.featured ? 'text-white' : 'text-slate-200'
|
training.featured ? "text-white" : "text-slate-200"
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{training.description.split(" ").splice(0, 40).join(" ") + "..."}
|
{training.description.split(" ").splice(0, 40).join(" ") + "..."}
|
||||||
</p>
|
</p>
|
||||||
<Button
|
<Button
|
||||||
href={"/skoleni/" + training.slug}
|
href={"/skoleni/" + training.slug}
|
||||||
variant={training.featured ? 'solid' : 'outline'}
|
variant={training.featured ? "solid" : "outline"}
|
||||||
color="white"
|
color="white"
|
||||||
className="mt-8 w-full"
|
className="mt-8 w-full"
|
||||||
>
|
>
|
||||||
Zjistit více
|
Zjistit více
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
type TrainingListProps = {
|
type TrainingListProps = {
|
||||||
trainingList: TrainingType[]
|
trainingList: TrainingType[];
|
||||||
}
|
};
|
||||||
|
|
||||||
const TrainingListMobile = ({ trainingList }: TrainingListProps) => {
|
const TrainingListMobile = ({ trainingList }: TrainingListProps) => {
|
||||||
return (
|
return (
|
||||||
<div className="-mx-4 mt-20 flex flex-col gap-y-10 overflow-hidden sm:-mx-6 sm:px-6 lg:hidden">
|
<div className="-mx-4 mt-20 flex flex-col gap-y-10 overflow-hidden sm:-mx-6 sm:px-6 lg:hidden">
|
||||||
{trainingList.map((training) => (
|
{trainingList.map((training) => (
|
||||||
<div key={training.slug} className={clsx(training.featured ? 'order-first lg:order-none' : '')}>
|
<div
|
||||||
|
key={training.slug}
|
||||||
|
className={clsx(training.featured ? "order-first lg:order-none" : "")}
|
||||||
|
>
|
||||||
<TrainingDetail training={training} className="mx-auto max-w-2xl" />
|
<TrainingDetail training={training} className="mx-auto max-w-2xl" />
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
const TrainingListDesktop = ({ trainingList }: TrainingListProps) => {
|
const TrainingListDesktop = ({ trainingList }: TrainingListProps) => {
|
||||||
return (
|
return (
|
||||||
<div className="hidden lg:mt-20 lg:block">
|
<div className="hidden lg:mt-20 lg:block">
|
||||||
<div className="grid grid-cols-3 gap-x-8 gap-y-8"
|
<div className="grid grid-cols-3 gap-x-8 gap-y-8">
|
||||||
>
|
|
||||||
{trainingList.map((training) => (
|
{trainingList.map((training) => (
|
||||||
<div key={training.slug} className={clsx(training.featured ? 'order-first lg:order-none' : '')}>
|
<div
|
||||||
|
key={training.slug}
|
||||||
|
className={clsx(
|
||||||
|
training.featured ? "order-first lg:order-none" : ""
|
||||||
|
)}
|
||||||
|
>
|
||||||
<TrainingDetail training={training} className="relative" />
|
<TrainingDetail training={training} className="relative" />
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
export const TrainingListGrid = ({ trainingList }: TrainingListProps) => {
|
export const TrainingListGrid = ({ trainingList }: TrainingListProps) => {
|
||||||
return (
|
return (
|
||||||
|
|
@ -97,7 +115,7 @@ export const TrainingListGrid = ({ trainingList }: TrainingListProps) => {
|
||||||
<section
|
<section
|
||||||
id="training-list"
|
id="training-list"
|
||||||
aria-label="Seznam školení"
|
aria-label="Seznam školení"
|
||||||
className="pt-16 pb-14 sm:pb-20 sm:pt-24 lg:pb-32 bg-slate-900"
|
className="bg-slate-900 pt-16 pb-14 sm:pb-20 sm:pt-24 lg:pb-32"
|
||||||
>
|
>
|
||||||
<Container>
|
<Container>
|
||||||
<div className="mx-auto max-w-2xl md:text-center">
|
<div className="mx-auto max-w-2xl md:text-center">
|
||||||
|
|
@ -105,7 +123,8 @@ export const TrainingListGrid = ({ trainingList }: TrainingListProps) => {
|
||||||
Moje školení
|
Moje školení
|
||||||
</h2>
|
</h2>
|
||||||
<p className="mt-4 text-lg tracking-tight text-slate-400">
|
<p className="mt-4 text-lg tracking-tight text-slate-400">
|
||||||
Od veřejného cloudu přes on-premise až po serverless, se vším vám poradím.
|
Od veřejného cloudu přes on-premise až po serverless, se vším vám
|
||||||
|
poradím.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<TrainingListMobile trainingList={trainingList} />
|
<TrainingListMobile trainingList={trainingList} />
|
||||||
|
|
@ -113,5 +132,5 @@ export const TrainingListGrid = ({ trainingList }: TrainingListProps) => {
|
||||||
</Container>
|
</Container>
|
||||||
</section>
|
</section>
|
||||||
</>
|
</>
|
||||||
)
|
);
|
||||||
}
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,52 +1,51 @@
|
||||||
import fs from 'fs'
|
import fs from "fs";
|
||||||
import { join } from 'path'
|
import { join } from "path";
|
||||||
import matter from 'gray-matter'
|
import matter from "gray-matter";
|
||||||
|
|
||||||
const trainingDir = join(process.cwd(), 'content/training')
|
const trainingDir = join(process.cwd(), "content/training");
|
||||||
|
|
||||||
export const getTrainingSlugs = () => {
|
export const getTrainingSlugs = () => {
|
||||||
return fs.readdirSync(trainingDir)
|
return fs.readdirSync(trainingDir);
|
||||||
}
|
};
|
||||||
|
|
||||||
export const getTrainingBySlug = (slug: string, fields: string[] = []) => {
|
export const getTrainingBySlug = (slug: string, fields: string[] = []) => {
|
||||||
const realSlug = slug.replace(/\.md$/, '')
|
const realSlug = slug.replace(/\.md$/, "");
|
||||||
const fullPath = join(trainingDir, `${realSlug}.md`)
|
const fullPath = join(trainingDir, `${realSlug}.md`);
|
||||||
const fileContents = fs.readFileSync(fullPath, 'utf8')
|
const fileContents = fs.readFileSync(fullPath, "utf8");
|
||||||
const { data, content } = matter(fileContents)
|
const { data, content } = matter(fileContents);
|
||||||
|
|
||||||
type Items = {
|
type Items = {
|
||||||
[key: string]: string
|
[key: string]: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
const items: Items = {}
|
const items: Items = {};
|
||||||
|
|
||||||
// Ensure only the minimal needed data is exposed
|
// Ensure only the minimal needed data is exposed
|
||||||
fields.forEach((field) => {
|
fields.forEach((field) => {
|
||||||
if (field === 'slug') {
|
if (field === "slug") {
|
||||||
items[field] = realSlug
|
items[field] = realSlug;
|
||||||
}
|
}
|
||||||
if (field === 'content') {
|
if (field === "content") {
|
||||||
items[field] = content
|
items[field] = content;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof data[field] !== 'undefined') {
|
if (typeof data[field] !== "undefined") {
|
||||||
items[field] = data[field]
|
items[field] = data[field];
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
|
|
||||||
return items
|
return items;
|
||||||
|
};
|
||||||
}
|
|
||||||
|
|
||||||
export const getAllTraining = (fields: string[] = []) => {
|
export const getAllTraining = (fields: string[] = []) => {
|
||||||
const slugs = getTrainingSlugs()
|
const slugs = getTrainingSlugs();
|
||||||
const trainingList = slugs
|
const trainingList = slugs
|
||||||
.map((slug) => getTrainingBySlug(slug, fields))
|
.map((slug) => getTrainingBySlug(slug, fields))
|
||||||
.sort((tr1, tr2) => {
|
.sort((tr1, tr2) => {
|
||||||
const tr1w = parseInt(tr1.weight)
|
const tr1w = parseInt(tr1.weight);
|
||||||
const tr2w = parseInt(tr2.weight)
|
const tr2w = parseInt(tr2.weight);
|
||||||
return tr1w > tr2w ? -1 : 1
|
return tr1w > tr2w ? -1 : 1;
|
||||||
})
|
});
|
||||||
|
|
||||||
return trainingList
|
return trainingList;
|
||||||
}
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
import { remark } from 'remark'
|
import { remark } from "remark";
|
||||||
import html from 'remark-html'
|
import html from "remark-html";
|
||||||
|
|
||||||
const markdownToHTML = async (markdown: string) => {
|
const markdownToHTML = async (markdown: string) => {
|
||||||
const result = await remark().use(html).process(markdown)
|
const result = await remark().use(html).process(markdown);
|
||||||
return result.toString()
|
return result.toString();
|
||||||
}
|
};
|
||||||
|
|
||||||
export default markdownToHTML
|
export default markdownToHTML;
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
import { AppProps } from 'next/app';
|
import { AppProps } from "next/app";
|
||||||
|
|
||||||
import '@/css/tailwind.css';
|
import "@/css/tailwind.css";
|
||||||
|
|
||||||
function App({ Component, pageProps }: AppProps) {
|
function App({ Component, pageProps }: AppProps) {
|
||||||
return (<Component {...pageProps} />);
|
return <Component {...pageProps} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default App;
|
export default App;
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { Head, Html, Main, NextScript } from 'next/document'
|
import { Head, Html, Main, NextScript } from "next/document";
|
||||||
|
|
||||||
export default function Document(props: any) {
|
export default function Document(props: any) {
|
||||||
let pageProps = props.__NEXT_DATA__?.props?.pageProps
|
let pageProps = props.__NEXT_DATA__?.props?.pageProps;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Html
|
<Html
|
||||||
|
|
@ -19,19 +19,37 @@ export default function Document(props: any) {
|
||||||
rel="stylesheet"
|
rel="stylesheet"
|
||||||
href="https://fonts.googleapis.com/css2?family=Inter:wght@100..900&family=Lexend:wght@400;500&display=swap"
|
href="https://fonts.googleapis.com/css2?family=Inter:wght@100..900&family=Lexend:wght@400;500&display=swap"
|
||||||
/> */}
|
/> */}
|
||||||
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
|
<link
|
||||||
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png" />
|
rel="apple-touch-icon"
|
||||||
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png" />
|
sizes="180x180"
|
||||||
|
href="/apple-touch-icon.png"
|
||||||
|
/>
|
||||||
|
<link
|
||||||
|
rel="icon"
|
||||||
|
type="image/png"
|
||||||
|
sizes="32x32"
|
||||||
|
href="/favicon-32x32.png"
|
||||||
|
/>
|
||||||
|
<link
|
||||||
|
rel="icon"
|
||||||
|
type="image/png"
|
||||||
|
sizes="16x16"
|
||||||
|
href="/favicon-16x16.png"
|
||||||
|
/>
|
||||||
<link rel="manifest" href="/site.webmanifest" />
|
<link rel="manifest" href="/site.webmanifest" />
|
||||||
<link rel="mask-icon" href="/safari-pinned-tab.svg" color="#5bbad5" />
|
<link rel="mask-icon" href="/safari-pinned-tab.svg" color="#5bbad5" />
|
||||||
<meta name="msapplication-TileColor" content="#da532c" />
|
<meta name="msapplication-TileColor" content="#da532c" />
|
||||||
<meta name="theme-color" content="#ffffff" />
|
<meta name="theme-color" content="#ffffff" />
|
||||||
<script defer data-domain="vojtechmares.com" src="https://plausible.io/js/script.js"></script>
|
<script
|
||||||
|
defer
|
||||||
|
data-domain="vojtechmares.com"
|
||||||
|
src="https://plausible.io/js/script.js"
|
||||||
|
></script>
|
||||||
</Head>
|
</Head>
|
||||||
<body className="flex h-full flex-col">
|
<body className="flex h-full flex-col">
|
||||||
<Main />
|
<Main />
|
||||||
<NextScript />
|
<NextScript />
|
||||||
</body>
|
</body>
|
||||||
</Html>
|
</Html>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,15 @@
|
||||||
import Head from "next/head";
|
import Head from "next/head";
|
||||||
|
|
||||||
import { Header } from "@/components/Header"
|
import { Header } from "@/components/Header";
|
||||||
import { Hero } from "@/components/Hero"
|
import { Hero } from "@/components/Hero";
|
||||||
import { CallToAction } from '@/components/CallToAction'
|
import { CallToAction } from "@/components/CallToAction";
|
||||||
import { Faqs } from '@/components/Faqs'
|
import { Faqs } from "@/components/Faqs";
|
||||||
import { Footer } from '@/components/Footer'
|
import { Footer } from "@/components/Footer";
|
||||||
import { Pricing } from '@/components/Pricing'
|
import { Pricing } from "@/components/Pricing";
|
||||||
import { PrimaryFeatures } from '@/components/PrimaryFeatures'
|
import { PrimaryFeatures } from "@/components/PrimaryFeatures";
|
||||||
import { TechStack } from '@/components/TechStack'
|
import { TechStack } from "@/components/TechStack";
|
||||||
import { Testimonials } from '@/components/Testimonials'
|
import { Testimonials } from "@/components/Testimonials";
|
||||||
import { Environment } from '@/components/Environment'
|
import { Environment } from "@/components/Environment";
|
||||||
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
return (
|
return (
|
||||||
|
|
|
||||||
|
|
@ -1,24 +1,24 @@
|
||||||
import Head from "next/head"
|
import Head from "next/head";
|
||||||
import { useRouter } from "next/router"
|
import { useRouter } from "next/router";
|
||||||
import ErrorPage from "next/error"
|
import ErrorPage from "next/error";
|
||||||
|
|
||||||
import TrainingType from "@/types/training"
|
import TrainingType from "@/types/training";
|
||||||
import { getAllTraining, getTrainingBySlug } from "@/lib/cms/training"
|
import { getAllTraining, getTrainingBySlug } from "@/lib/cms/training";
|
||||||
import markdownToHTML from "@/lib/markdownToHTML"
|
import markdownToHTML from "@/lib/markdownToHTML";
|
||||||
import { Header } from "@/components/Header"
|
import { Header } from "@/components/Header";
|
||||||
import { Footer } from "@/components/Footer"
|
import { Footer } from "@/components/Footer";
|
||||||
import { Training } from "@/components/Training"
|
import { Training } from "@/components/Training";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
training: TrainingType
|
training: TrainingType;
|
||||||
featuredTrainingList: TrainingType[]
|
featuredTrainingList: TrainingType[];
|
||||||
}
|
};
|
||||||
|
|
||||||
const TrainingPage = ({ training, featuredTrainingList }: Props) => {
|
const TrainingPage = ({ training, featuredTrainingList }: Props) => {
|
||||||
const router = useRouter()
|
const router = useRouter();
|
||||||
console.log(!router.isFallback)
|
console.log(!router.isFallback);
|
||||||
console.log(!training?.slug)
|
console.log(!training?.slug);
|
||||||
console.log(typeof training.draft === 'undefined')
|
console.log(typeof training.draft === "undefined");
|
||||||
// if ((!router.isFallback && !training?.slug) || typeof training.draft === 'undefined') {
|
// if ((!router.isFallback && !training?.slug) || typeof training.draft === 'undefined') {
|
||||||
// return <ErrorPage statusCode={404} />
|
// return <ErrorPage statusCode={404} />
|
||||||
// }
|
// }
|
||||||
|
|
@ -26,7 +26,10 @@ const TrainingPage = ({ training, featuredTrainingList}: Props) => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Head>
|
<Head>
|
||||||
<title>Školení {training.name} | Vojtěch Mareš - DevOps konzultant, lektor, engineer</title>
|
<title>
|
||||||
|
Školení {training.name} | Vojtěch Mareš - DevOps konzultant, lektor,
|
||||||
|
engineer
|
||||||
|
</title>
|
||||||
|
|
||||||
<meta
|
<meta
|
||||||
name="description"
|
name="description"
|
||||||
|
|
@ -39,54 +42,54 @@ const TrainingPage = ({ training, featuredTrainingList}: Props) => {
|
||||||
</main>
|
</main>
|
||||||
<Footer />
|
<Footer />
|
||||||
</>
|
</>
|
||||||
)
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
export default TrainingPage
|
export default TrainingPage;
|
||||||
|
|
||||||
type Params = {
|
type Params = {
|
||||||
params: {
|
params: {
|
||||||
slug: string
|
slug: string;
|
||||||
}
|
};
|
||||||
}
|
};
|
||||||
|
|
||||||
export const getStaticProps = async ({ params }: Params) => {
|
export const getStaticProps = async ({ params }: Params) => {
|
||||||
const training = getTrainingBySlug(params.slug, [
|
const training = getTrainingBySlug(params.slug, [
|
||||||
'name',
|
"name",
|
||||||
'content',
|
"content",
|
||||||
'priceOpen',
|
"priceOpen",
|
||||||
'priceCompany',
|
"priceCompany",
|
||||||
'days',
|
"days",
|
||||||
'logo',
|
"logo",
|
||||||
'content',
|
"content",
|
||||||
'draft',
|
"draft",
|
||||||
'new',
|
"new",
|
||||||
'featured',
|
"featured",
|
||||||
])
|
]);
|
||||||
|
|
||||||
const content = await markdownToHTML(training.content || '')
|
const content = await markdownToHTML(training.content || "");
|
||||||
|
|
||||||
return {
|
return {
|
||||||
props: {
|
props: {
|
||||||
training: {
|
training: {
|
||||||
...training,
|
...training,
|
||||||
content,
|
content,
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
};
|
||||||
}
|
};
|
||||||
|
|
||||||
export const getStaticPaths = async () => {
|
export const getStaticPaths = async () => {
|
||||||
const trainingList = getAllTraining(['slug'])
|
const trainingList = getAllTraining(["slug"]);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
paths: trainingList.map((training) => {
|
paths: trainingList.map((training) => {
|
||||||
return {
|
return {
|
||||||
params: {
|
params: {
|
||||||
slug: training.slug,
|
slug: training.slug,
|
||||||
}
|
},
|
||||||
}
|
};
|
||||||
}),
|
}),
|
||||||
fallback: false,
|
fallback: false,
|
||||||
}
|
};
|
||||||
}
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,41 +1,43 @@
|
||||||
import Head from "next/head";
|
import Head from "next/head";
|
||||||
|
|
||||||
import { Header } from "@/components/Header"
|
import { Header } from "@/components/Header";
|
||||||
import { Footer } from '@/components/Footer'
|
import { Footer } from "@/components/Footer";
|
||||||
import { getAllTraining } from "@/lib/cms/training";
|
import { getAllTraining } from "@/lib/cms/training";
|
||||||
import TrainingType from "@/types/training";
|
import TrainingType from "@/types/training";
|
||||||
import { TrainingListGrid } from "@/components/TrainingListGrid";
|
import { TrainingListGrid } from "@/components/TrainingListGrid";
|
||||||
|
|
||||||
export const getStaticProps = async () => {
|
export const getStaticProps = async () => {
|
||||||
const allTraining = getAllTraining([
|
const allTraining = getAllTraining([
|
||||||
'name',
|
"name",
|
||||||
'slug',
|
"slug",
|
||||||
'logo',
|
"logo",
|
||||||
'description',
|
"description",
|
||||||
'days',
|
"days",
|
||||||
'weight',
|
"weight",
|
||||||
'featured',
|
"featured",
|
||||||
'new',
|
"new",
|
||||||
'draft',
|
"draft",
|
||||||
])
|
]);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
props: { allTraining },
|
props: { allTraining },
|
||||||
}
|
};
|
||||||
}
|
};
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
allTraining: TrainingType[]
|
allTraining: TrainingType[];
|
||||||
}
|
};
|
||||||
|
|
||||||
const TrainingList = ({ allTraining }: Props) => {
|
const TrainingList = ({ allTraining }: Props) => {
|
||||||
// remove drafts
|
// remove drafts
|
||||||
const trainingList = allTraining.filter((val) => !val.draft)
|
const trainingList = allTraining.filter((val) => !val.draft);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Head>
|
<Head>
|
||||||
<title>Moje školení | Vojtěch Mareš - DevOps konzultant, lektor, engineer</title>
|
<title>
|
||||||
|
Moje školení | Vojtěch Mareš - DevOps konzultant, lektor, engineer
|
||||||
|
</title>
|
||||||
|
|
||||||
<meta
|
<meta
|
||||||
name="description"
|
name="description"
|
||||||
|
|
@ -49,6 +51,6 @@ const TrainingList = ({ allTraining }: Props) => {
|
||||||
<Footer />
|
<Footer />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
export default TrainingList
|
export default TrainingList;
|
||||||
|
|
|
||||||
4
prettier.config.cjs
Normal file
4
prettier.config.cjs
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
/** @type {import("prettier").Config} */
|
||||||
|
module.exports = {
|
||||||
|
plugins: [require.resolve("prettier-plugin-tailwindcss")],
|
||||||
|
};
|
||||||
|
|
@ -1,19 +1,19 @@
|
||||||
import { expect, test } from 'vitest'
|
import { expect, test } from "vitest";
|
||||||
import { render, screen, cleanup } from '@testing-library/react'
|
import { render, screen, cleanup } from "@testing-library/react";
|
||||||
import { Button } from '@/components/Button'
|
import { Button } from "@/components/Button";
|
||||||
|
|
||||||
test('button', () => {
|
test("button", () => {
|
||||||
render(<Button>Hello</Button>)
|
render(<Button>Hello</Button>);
|
||||||
|
|
||||||
expect(screen.getByText('Hello')).toBeDefined()
|
expect(screen.getByText("Hello")).toBeDefined();
|
||||||
|
|
||||||
cleanup()
|
cleanup();
|
||||||
})
|
});
|
||||||
|
|
||||||
test('button-with-link', () => {
|
test("button-with-link", () => {
|
||||||
render(<Button href="/country-road">Hello</Button>)
|
render(<Button href="/country-road">Hello</Button>);
|
||||||
|
|
||||||
expect(screen.getByText('Hello').getAttribute('href')).toBe('/country-road')
|
expect(screen.getByText("Hello").getAttribute("href")).toBe("/country-road");
|
||||||
|
|
||||||
cleanup()
|
cleanup();
|
||||||
})
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,25 @@
|
||||||
import { expect, test } from 'vitest'
|
import { expect, test } from "vitest";
|
||||||
import { render, screen, within } from '@testing-library/react'
|
import { render, screen, within } from "@testing-library/react";
|
||||||
import Home from '../pages'
|
import Home from "../pages";
|
||||||
|
|
||||||
test('home', () => {
|
test("home", () => {
|
||||||
render(<Home />)
|
render(<Home />);
|
||||||
const main = within(screen.getByRole('main'))
|
const main = within(screen.getByRole("main"));
|
||||||
expect(main.getByRole('heading', { level: 1, name: /DevOps jednoduše pro všechny./i })).toBeDefined()
|
expect(
|
||||||
|
main.getByRole("heading", {
|
||||||
|
level: 1,
|
||||||
|
name: /DevOps jednoduše pro všechny./i,
|
||||||
|
})
|
||||||
|
).toBeDefined();
|
||||||
|
|
||||||
const meets = screen.getAllByText(/Domluvme si schůzku/i)
|
const meets = screen.getAllByText(/Domluvme si schůzku/i);
|
||||||
meets.map(
|
meets.map((m) =>
|
||||||
(m) => expect(m.getAttribute('href'))
|
expect(m.getAttribute("href")).toBe(
|
||||||
.toBe('https://calendly.com/vojtechmares/30min')
|
"https://calendly.com/vojtechmares/30min"
|
||||||
)
|
)
|
||||||
|
);
|
||||||
|
|
||||||
// const footer = within(screen.getByRole('contentinfo'))
|
// const footer = within(screen.getByRole('contentinfo'))
|
||||||
// const link = within(footer.getByRole('link'))
|
// const link = within(footer.getByRole('link'))
|
||||||
// expect(link.getByRole('img', { name: /vercel logo/i })).toBeDefined()
|
// expect(link.getByRole('img', { name: /vercel logo/i })).toBeDefined()
|
||||||
})
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,18 @@
|
||||||
type TrainingType = {
|
type TrainingType = {
|
||||||
name: string
|
name: string;
|
||||||
slug: string
|
slug: string;
|
||||||
priceOpen: number
|
priceOpen: number;
|
||||||
priceCompany: number
|
priceCompany: number;
|
||||||
logo: string
|
logo: string;
|
||||||
days: number
|
days: number;
|
||||||
description: string
|
description: string;
|
||||||
content: string
|
content: string;
|
||||||
|
|
||||||
trainingHubId?: string
|
trainingHubId?: string;
|
||||||
draft?: true
|
draft?: true;
|
||||||
new?: true
|
new?: true;
|
||||||
featured?: true
|
featured?: true;
|
||||||
weight: number // custom sorting
|
weight: number; // custom sorting
|
||||||
}
|
};
|
||||||
|
|
||||||
export default TrainingType
|
export default TrainingType;
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,18 @@
|
||||||
/// <reference types="vitest" />
|
/// <reference types="vitest" />
|
||||||
|
|
||||||
import { defineConfig } from 'vitest/config'
|
import { defineConfig } from "vitest/config";
|
||||||
import react from '@vitejs/plugin-react'
|
import react from "@vitejs/plugin-react";
|
||||||
import { resolve } from 'path'
|
import { resolve } from "path";
|
||||||
|
|
||||||
// https://vitejs.dev/config/
|
// https://vitejs.dev/config/
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [
|
plugins: [react()],
|
||||||
react(),
|
|
||||||
],
|
|
||||||
test: {
|
test: {
|
||||||
environment: 'jsdom',
|
environment: "jsdom",
|
||||||
},
|
},
|
||||||
resolve: {
|
resolve: {
|
||||||
alias: {
|
alias: {
|
||||||
'@': resolve(__dirname, '.')
|
"@": resolve(__dirname, "."),
|
||||||
}
|
|
||||||
},
|
},
|
||||||
})
|
},
|
||||||
|
});
|
||||||
|
|
|
||||||
Reference in a new issue