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
6 changes: 6 additions & 0 deletions .server-changes/hipaa-addon-pricing-cta.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
area: webapp
type: feature
---

Request a HIPAA BAA add-on directly from any paid pricing tier in the dashboard.
4 changes: 3 additions & 1 deletion apps/webapp/app/components/DefinitionTooltip.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,16 @@ export function DefinitionTip({
content,
children,
title,
disableHoverableContent = true,
}: {
content: React.ReactNode;
children: React.ReactNode;
title: React.ReactNode;
disableHoverableContent?: boolean;
}) {
return (
<TooltipProvider>
<Tooltip disableHoverableContent>
<Tooltip disableHoverableContent={disableHoverableContent}>
<TooltipTrigger className="text-left">
<span className="cursor-default underline decoration-charcoal-500 decoration-dashed underline-offset-4 transition hover:decoration-charcoal-400">
{children}
Expand Down
32 changes: 24 additions & 8 deletions apps/webapp/app/components/Feedback.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { conform, useForm } from "@conform-to/react";
import { parse } from "@conform-to/zod";
import { InformationCircleIcon, ArrowUpCircleIcon } from "@heroicons/react/20/solid";
import { EnvelopeIcon } from "@heroicons/react/24/solid";
import { EnvelopeIcon, ShieldCheckIcon } from "@heroicons/react/24/solid";
import { Form, useActionData, useLocation, useNavigation, useSearchParams } from "@remix-run/react";
import { type ReactNode, useEffect, useState } from "react";
import { type FeedbackType, feedbackTypeLabel, schema } from "~/routes/resources.feedback";
import { type FeedbackType, feedbackTypes, schema } from "~/routes/resources.feedback";
import { Button } from "./primitives/Buttons";
import { Dialog, DialogContent, DialogHeader, DialogTrigger } from "./primitives/Dialog";
import { Fieldset } from "./primitives/Fieldset";
Expand Down Expand Up @@ -84,9 +84,12 @@ export function Feedback({ button, defaultValue = "bug", onOpenChange }: Feedbac
How can we help? We read every message and will respond as quickly as we can.
</Paragraph>
</div>
{!(type === "feature" || type === "help" || type === "concurrency") && (
<hr className="border-grid-dimmed" />
)}
{!(
type === "feature" ||
type === "help" ||
type === "concurrency" ||
type === "hipaa"
) && <hr className="border-grid-dimmed" />}
<Form method="post" action="/resources/feedback" {...form.props} className="w-full">
<Fieldset className="max-w-full gap-y-3">
<input value={location.pathname} {...conform.input(path, { type: "hidden" })} />
Expand Down Expand Up @@ -132,19 +135,32 @@ export function Feedback({ button, defaultValue = "bug", onOpenChange }: Feedbac
</Paragraph>
</InfoPanel>
)}
{type === "hipaa" && (
<InfoPanel
icon={ShieldCheckIcon}
iconClassName="text-green-500"
panelClassName="w-full mb-2"
>
<Paragraph variant="small">
We offer a signed Business Associate Agreement (BAA) as a paid add-on on any
paid plan. To help us get back to you quickly, please include your company
name, and a brief description of the PHI workload you plan to run.
</Paragraph>
</InfoPanel>
)}
<Select
{...conform.select(feedbackType)}
variant="tertiary/medium"
value={type}
defaultValue={type}
setValue={(v) => setType(v as FeedbackType)}
placeholder="Select type"
text={(value) => feedbackTypeLabel[value as FeedbackType]}
text={(value) => feedbackTypes[value as FeedbackType].label}
dropdownIcon
>
{Object.entries(feedbackTypeLabel).map(([name, title]) => (
{Object.entries(feedbackTypes).map(([name, { label }]) => (
<SelectItem key={name} value={name}>
{title}
{label}
</SelectItem>
))}
</Select>
Expand Down
65 changes: 51 additions & 14 deletions apps/webapp/app/routes/resources.feedback.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,55 @@ import { sendToPlain } from "~/utils/plain.server";

let client: PlainClient | undefined;

export const feedbackTypeLabel = {
bug: "Bug report",
feature: "Feature request",
help: "Help me out",
enterprise: "Enterprise enquiry",
feedback: "General feedback",
concurrency: "Increase my concurrency",
region: "Suggest a new region",
};
export const feedbackTypes = {
bug: {
label: "Bug report",
labelTypeId: "lt_01HB920BTPFS36KH1JT9C36YVY",
threadTitle: "Contact form: Bug report",
},
feature: {
label: "Feature request",
labelTypeId: "lt_01HB920BV8CJGYXVE15WWN6P07",
threadTitle: "Contact form: Feature request",
},
help: {
label: "Help me out",
labelTypeId: "lt_01KTVCAPZY5ZJ0SS4ACMXWYYT3",
threadTitle: "Contact form: Help me out",
},
enterprise: {
label: "Enterprise enquiry",
labelTypeId: "lt_01K7PF5EV2877EH4SZYB667FW4",
threadTitle: "Contact form: Enterprise enquiry",
},
feedback: {
label: "General feedback",
labelTypeId: "lt_01HB920BSRZ3RA1ETHBVEB5ST2",
threadTitle: "Contact form: General feedback",
},
concurrency: {
label: "Increase my concurrency",
labelTypeId: "lt_01KTVCCY2PDE5V6WV2PQ8N85K2",
threadTitle: "Contact form: Increase my concurrency",
},
region: {
label: "Suggest a new region",
labelTypeId: "lt_01KTVCDPYYBW6KS9H5V8MTQ0GG",
threadTitle: "Contact form: Suggest a new region",
},
hipaa: {
label: "HIPAA BAA request",
labelTypeId: "lt_01KS54WBRYKE6DY369KPK2SS4W",
threadTitle: "Contact form: HIPAA BAA request",
},
} as const satisfies Record<
string,
{ label: string; labelTypeId?: string; threadTitle: string }
>;

export type FeedbackType = keyof typeof feedbackTypeLabel;
export type FeedbackType = keyof typeof feedbackTypes;

const feedbackTypeLiterals = Object.keys(feedbackTypeLabel).map((key) => z.literal(key));
const feedbackTypeLiterals = Object.keys(feedbackTypes).map((key) => z.literal(key));

const feedbackType = z.union(
[feedbackTypeLiterals[0], feedbackTypeLiterals[1], ...feedbackTypeLiterals.slice(2)],
Expand All @@ -46,16 +82,17 @@ export async function action({ request }: ActionFunctionArgs) {
return json(submission);
}

const title = feedbackTypeLabel[submission.value.feedbackType as FeedbackType];
const inquiry = feedbackTypes[submission.value.feedbackType as FeedbackType];
try {
await sendToPlain({
userId: user.id,
email: user.email,
name: user.name ?? user.displayName ?? user.email,
title,
title: inquiry.threadTitle,
Comment thread
D-K-P marked this conversation as resolved.
labelTypeIds: inquiry.labelTypeId ? [inquiry.labelTypeId] : undefined,
components: [
uiComponent.text({
text: `New ${title} reported by ${user.name} (${user.email})`,
text: `New ${inquiry.label} reported by ${user.name} (${user.email})`,
}),
uiComponent.divider({ spacingSize: "M" }),
uiComponent.text({
Expand Down
Loading
Loading