Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 21 additions & 49 deletions website/app/(content)/help/page.tsx
Original file line number Diff line number Diff line change
@@ -1,60 +1,32 @@
import { Offering } from "@/src/components/Offering";
import { OfferingGrid } from "@/src/components/OfferingGrid";
import { Section } from "@/src/components/Section";
import { HelpClosing } from "@/src/components/help/HelpClosing";
import { HelpFaq } from "@/src/components/help/HelpFaq";
import { HelpHero } from "@/src/components/help/HelpHero";
import { HelpTiers } from "@/src/components/help/HelpTiers";
import { SelfServeGrid } from "@/src/components/help/SelfServeGrid";
import { pageMetadata } from "@/src/helpers/pageMetadata";

export const metadata = pageMetadata({
title: "Help",
title: "Get GraphQL help fast",
description:
"Need urgent GraphQL help? Join the ChilliCream community Slack, book a session with an expert, or choose a support plan that fits your team.",
"Stuck on GraphQL? Get help from the ChilliCream community, book an expert consultancy session, or pick a support plan. Pick the path that fits the urgency.",
keywords: [
"GraphQL help",
"ChilliCream support",
"Hot Chocolate consultancy",
"GraphQL Slack community",
"GraphQL support plan",
],
path: "/help",
});

const PLANS = [
{
title: "Community",
price: "Free",
description:
"Be part of the Community, get help, and help others. Together we're strong.",
perks: ["Public Slack Channel", "7000+ Individuals"],
callToAction: {
title: "Join Slack",
link: "https://slack.chillicream.com/",
},
},
{
title: "Consultancy",
price: "$300",
priceNote: "per hour",
description: "You need immediate help and want to talk to an Expert.",
perks: ["Dedicated Session", "Dedicated Expert"],
callToAction: {
title: "Book a Session",
link: "https://calendly.com/chillicream/60min",
},
},
{
title: "Support",
price: "Custom",
description: "You need a Support Plan. Here you go.",
perks: [
"Dedicated Account Manager",
"Private Slack Channel",
"E-Mail Support",
"And More...",
],
callToAction: { title: "Check out Plans", link: "/services/support" },
},
];

export default function HelpPage() {
return (
<Section title="In Need of Urgent Help">
<OfferingGrid columns="md:grid-cols-2 lg:grid-cols-3">
{PLANS.map((plan) => (
<Offering key={plan.title} {...plan} />
))}
</OfferingGrid>
</Section>
<>
<HelpHero />
<HelpTiers />
<SelfServeGrid />
<HelpFaq />
<HelpClosing />
</>
);
}
16 changes: 6 additions & 10 deletions website/app/(content)/pricing/page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import type { Metadata } from "next";

import { ButtonRow } from "@/src/components/ButtonRow";
import { MarketingHero } from "@/src/components/MarketingHero";
import { ClosingCta } from "@/src/components/pricing/ClosingCta";
Expand All @@ -10,13 +8,11 @@ import { RegulatedBand } from "@/src/components/pricing/RegulatedBand";
import { OutlineButton, SolidButton } from "@/src/design-system/Button";
import { pageMetadata } from "@/src/helpers/pageMetadata";

export const metadata: Metadata = {
...pageMetadata({
title: "Pricing",
description:
"Nitro pricing: start free on the shared cloud, pay as you go at $20/mo, run a dedicated single-tenant instance from $400, or self-host. Compare every plan.",
path: "/pricing",
}),
export const metadata = pageMetadata({
title: "Pricing",
description:
"Nitro pricing: start free on the shared cloud, pay as you go at $20/mo, run a dedicated single-tenant instance from $400, or self-host. Compare every plan.",
path: "/pricing",
keywords: [
"ChilliCream pricing",
"Nitro pricing",
Expand All @@ -27,7 +23,7 @@ export const metadata: Metadata = {
"schema registry pricing",
"GraphQL observability pricing",
],
};
});

export default function PricingPage() {
return (
Expand Down
16 changes: 6 additions & 10 deletions website/app/(content)/services/advisory/page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import type { Metadata } from "next";

import { AdvisoryFaq } from "@/src/components/advisory/AdvisoryFaq";
import { AdvisoryHero } from "@/src/components/advisory/AdvisoryHero";
import { ContactBand } from "@/src/components/advisory/ContactBand";
Expand All @@ -8,13 +6,11 @@ import { TeamSection } from "@/src/components/advisory/TeamSection";
import { TierGrid } from "@/src/components/advisory/TierGrid";
import { pageMetadata } from "@/src/helpers/pageMetadata";

export const metadata: Metadata = {
...pageMetadata({
title: "GraphQL Advisory",
description:
"GraphQL consulting in packages of hours, or full contracting, from the team behind Hot Chocolate, Fusion, and Nitro. Talk to an expert about your project.",
path: "/services/advisory",
}),
export const metadata = pageMetadata({
title: "GraphQL Advisory",
description:
"GraphQL consulting in packages of hours, or full contracting, from the team behind Hot Chocolate, Fusion, and Nitro. Talk to an expert about your project.",
path: "/services/advisory",
keywords: [
"GraphQL advisory",
"GraphQL consulting",
Expand All @@ -24,7 +20,7 @@ export const metadata: Metadata = {
"Nitro consulting",
"ChilliCream advisory",
],
};
});

export default function AdvisoryPage() {
return (
Expand Down
53 changes: 26 additions & 27 deletions website/app/(content)/services/support/contact/ContactForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -129,31 +129,29 @@ export function ContactForm() {
}

return (
<form
onSubmit={handleSubmit}
noValidate
className="border-cc-card-border bg-cc-card-bg flex flex-col gap-5 rounded-xl border p-8 backdrop-blur-sm"
>
<Input
label="Name"
name="name"
type="text"
required
value={data.name}
error={errors.name}
disabled={isSubmitting}
onChange={(e) => update("name", e.target.value)}
/>
<Input
label="Email"
name="email"
type="email"
required
value={data.email}
error={errors.email}
disabled={isSubmitting}
onChange={(e) => update("email", e.target.value)}
/>
<form onSubmit={handleSubmit} noValidate className="flex flex-col gap-5">
<div className="grid gap-5 sm:grid-cols-2">
<Input
label="Name"
name="name"
type="text"
required
value={data.name}
error={errors.name}
disabled={isSubmitting}
onChange={(e) => update("name", e.target.value)}
/>
<Input
label="Email"
name="email"
type="email"
required
value={data.email}
error={errors.email}
disabled={isSubmitting}
onChange={(e) => update("email", e.target.value)}
/>
</div>
<Input
label="Company"
name="company"
Expand Down Expand Up @@ -188,12 +186,13 @@ export function ContactForm() {
label="Message"
name="message"
rows={5}
placeholder="What are you building?"
value={data.message}
disabled={isSubmitting}
onChange={(e) => update("message", e.target.value)}
/>
<SolidButton type="submit" disabled={isSubmitting} className="self-start">
{isSubmitting ? "Sending..." : "Send"}
<SolidButton type="submit" disabled={isSubmitting} className="w-full">
{isSubmitting ? "Sending..." : "Talk to us"}
</SolidButton>
</form>
);
Expand Down
50 changes: 50 additions & 0 deletions website/app/(content)/services/support/contact/ContactIntro.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { CheckList } from "@/src/components/CheckList";
import { SlackIcon } from "@/src/icons/Slack";

const POINTS = [
"A reply within one business day",
"Straight to the core engineers",
"No sales runaround",
];

/**
* The left side of the contact panel: the pitch and what to expect, with the
* direct channels pinned to the bottom for people who would rather not use the
* form.
*/
export function ContactIntro() {
return (
<div className="flex h-full flex-col">
<p className="text-cc-accent font-mono text-xs font-semibold tracking-[0.18em] uppercase">
Talk to us
</p>
<h1 className="font-heading text-cc-heading mt-5 text-3xl font-semibold tracking-tight text-balance sm:text-4xl">
Tell us what you need.
</h1>
<p className="text-cc-ink-dim mt-5 text-base leading-relaxed text-pretty">
Wherever you are and wherever you&rsquo;re headed, you&rsquo;ll hear
back from the core team, not a first-line queue.
</p>
<CheckList className="mt-8" items={POINTS} />
<div className="mt-auto pt-10">
<div className="border-cc-card-border flex flex-col items-start gap-3 border-t pt-6 text-sm">
<a
href="mailto:contact@chillicream.com"
className="text-cc-ink hover:text-cc-accent transition-colors"
>
contact@chillicream.com
</a>
<a
href="https://slack.chillicream.com/"
target="_blank"
rel="noopener noreferrer"
className="text-cc-ink-dim hover:text-cc-accent inline-flex items-center gap-2 transition-colors"
>
<SlackIcon className="h-4 w-4 flex-none fill-current" />
Join the community Slack
</a>
</div>
</div>
</div>
);
}
32 changes: 22 additions & 10 deletions website/app/(content)/services/support/contact/page.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { PageHero } from "@/src/components/PageHero";
import { pageMetadata } from "@/src/helpers/pageMetadata";

import { ContactForm } from "./ContactForm";
import { ContactIntro } from "./ContactIntro";

export const metadata = pageMetadata({
title: "Contact Us",
Expand All @@ -10,16 +10,28 @@ export const metadata = pageMetadata({
path: "/services/support/contact",
});

// Cyan-to-coral brand spectrum, faded at both edges, for the panel's top hairline.
const SPECTRUM =
"linear-gradient(90deg, transparent, #16b9e4 30%, #7c92c6 50%, #f0786a 70%, transparent)";

export default function ContactPage() {
return (
<>
<PageHero
title="Contact Us"
teaser="Tell us a bit about what you need and we'll be in touch."
/>
<section className="mx-auto max-w-xl pb-16">
<ContactForm />
</section>
</>
<section className="py-12 sm:py-16">
<div className="border-cc-card-border bg-cc-card-bg/40 relative overflow-hidden rounded-3xl border">
<div
aria-hidden="true"
className="pointer-events-none absolute inset-x-0 top-0 h-px"
style={{ background: SPECTRUM }}
/>
<div className="divide-cc-card-border grid divide-y lg:grid-cols-2 lg:divide-x lg:divide-y-0">
<div className="p-8 sm:p-10 lg:p-12">
<ContactIntro />
</div>
<div className="p-8 sm:p-10 lg:p-12">
<ContactForm />
</div>
</div>
</div>
</section>
);
}
16 changes: 6 additions & 10 deletions website/app/(content)/services/support/page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import type { Metadata } from "next";

import { ClosingCta } from "@/src/components/support/ClosingCta";
import { ComparisonMatrix } from "@/src/components/support/ComparisonMatrix";
import { EnterpriseBand } from "@/src/components/support/EnterpriseBand";
Expand All @@ -8,13 +6,11 @@ import { SupportFaq } from "@/src/components/support/SupportFaq";
import { SupportHero } from "@/src/components/support/SupportHero";
import { pageMetadata } from "@/src/helpers/pageMetadata";

export const metadata: Metadata = {
...pageMetadata({
title: "GraphQL Support Plans",
description:
"Support and response-time plans from ChilliCream. Next business day on Startup and Business, 24 hours for Enterprise criticals, direct to the engineers who build Hot Chocolate, Fusion, and Nitro.",
path: "/services/support",
}),
export const metadata = pageMetadata({
title: "GraphQL Support Plans",
description:
"Support and response-time plans from ChilliCream. Next business day on Startup and Business, 24 hours for Enterprise criticals, direct to the engineers who build Hot Chocolate, Fusion, and Nitro.",
path: "/services/support",
keywords: [
"GraphQL support",
"HotChocolate support",
Expand All @@ -23,7 +19,7 @@ export const metadata: Metadata = {
"enterprise GraphQL",
"incident response",
],
};
});

export default function SupportPage() {
return (
Expand Down
14 changes: 5 additions & 9 deletions website/app/(content)/services/training/page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import type { Metadata } from "next";

import { DeliveryFormatsSection } from "@/src/components/training/DeliveryFormatsSection";
import { FunBand } from "@/src/components/training/FunBand";
import { LevelsSection } from "@/src/components/training/LevelsSection";
Expand All @@ -13,12 +11,10 @@ import { pageMetadata } from "@/src/helpers/pageMetadata";
const META_DESCRIPTION =
"Book GraphQL training for your team. Hot Chocolate, ASP.NET Core, React, and Relay curriculum that flexes for beginner, mixed, and advanced engineering teams.";

export const metadata: Metadata = {
...pageMetadata({
title: "GraphQL Training for Your Team",
description: META_DESCRIPTION,
path: "/services/training",
}),
export const metadata = pageMetadata({
title: "GraphQL Training for Your Team",
description: META_DESCRIPTION,
path: "/services/training",
keywords: [
"GraphQL training",
"Hot Chocolate training",
Expand All @@ -27,7 +23,7 @@ export const metadata: Metadata = {
"Relay training",
"ASP.NET Core GraphQL training",
],
};
});

export default function TrainingPage() {
return (
Expand Down
3 changes: 2 additions & 1 deletion website/app/sitemap.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import fs from "node:fs";
import path from "node:path";
import type { MetadataRoute } from "next";
import {
Expand All @@ -12,6 +11,8 @@ import { SITE_URL } from "@/src/helpers/siteUrl";

export const dynamic = "force-static";

const fs = process.getBuiltinModule("node:fs");

// Marketing / legal / product pages live in the `(content)` route group, so
// their on-disk folder names map 1:1 to URL paths (the group itself is elided).
const CONTENT_PAGES_ROOT = path.join(process.cwd(), "app", "(content)");
Expand Down
Loading
Loading