Replaced Upgrade callout with a more generic InfoPanel component
This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
import { LinkButton } from "./Buttons";
|
||||
import { Header3 } from "./Headers";
|
||||
import { Paragraph } from "./Paragraph";
|
||||
import { cn } from "~/utils/cn";
|
||||
|
||||
const variants = {
|
||||
info: {
|
||||
panelStyle: "border-grid-bright bg-background-bright",
|
||||
},
|
||||
upgrade: {
|
||||
panelStyle: "border-indigo-400/20 bg-indigo-800/10",
|
||||
},
|
||||
};
|
||||
|
||||
type InfoPanelVariant = keyof typeof variants;
|
||||
|
||||
type Props = {
|
||||
title?: string;
|
||||
children: React.ReactNode;
|
||||
to?: string;
|
||||
buttonLabel?: string;
|
||||
icon: React.ComponentType<any>;
|
||||
iconClassName?: string;
|
||||
variant?: InfoPanelVariant;
|
||||
panelClassName?: string;
|
||||
};
|
||||
|
||||
export function InfoPanel({
|
||||
title,
|
||||
children,
|
||||
to,
|
||||
buttonLabel,
|
||||
icon,
|
||||
iconClassName,
|
||||
variant = "info",
|
||||
panelClassName = "max-w-sm",
|
||||
}: Props) {
|
||||
const Icon = icon;
|
||||
const variantStyle = variants[variant];
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
variantStyle.panelStyle,
|
||||
title ? "flex-col" : "",
|
||||
"flex h-fit max-w-sm items-start gap-3 rounded-md border p-4",
|
||||
panelClassName
|
||||
)}
|
||||
>
|
||||
<div className={cn("flex items-center gap-2", to ? "w-full justify-between" : "")}>
|
||||
<Icon className={cn("size-5", iconClassName)} />
|
||||
|
||||
{to && (
|
||||
<LinkButton to={to} variant="secondary/small">
|
||||
{buttonLabel}
|
||||
</LinkButton>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex flex-col gap-1">
|
||||
{title && <Header3 className="text-text-bright">{title}</Header3>}
|
||||
{typeof children === "string" ? (
|
||||
<Paragraph variant={"small"} className="text-text-dimmed">
|
||||
{children}
|
||||
</Paragraph>
|
||||
) : (
|
||||
children
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
import { LockOpenIcon } from "@heroicons/react/24/solid";
|
||||
import { useOrganization } from "~/hooks/useOrganizations";
|
||||
import { v3BillingPath } from "~/utils/pathBuilder";
|
||||
import { LinkButton } from "./Buttons";
|
||||
import { Header3 } from "./Headers";
|
||||
import { Paragraph } from "./Paragraph";
|
||||
|
||||
type Props = {
|
||||
title: string;
|
||||
children: React.ReactNode;
|
||||
to?: string;
|
||||
};
|
||||
|
||||
export function UpgradeCallout({ title, children, to }: Props) {
|
||||
const organization = useOrganization();
|
||||
|
||||
to = to || v3BillingPath(organization);
|
||||
|
||||
return (
|
||||
<div
|
||||
className="flex w-fit flex-col items-start justify-between gap-3 rounded-md border border-indigo-400/20
|
||||
bg-indigo-800/10 p-4"
|
||||
>
|
||||
<div className="flex w-full items-center justify-between gap-2">
|
||||
<LockOpenIcon className="size-6 text-indigo-500" />
|
||||
<LinkButton to={to} variant="secondary/small">
|
||||
Upgrade
|
||||
</LinkButton>
|
||||
</div>
|
||||
<div className="flex flex-col gap-1">
|
||||
<Header3 className="text-text-bright">{title}</Header3>
|
||||
{typeof children === "string" ? (
|
||||
<Paragraph variant={"small"} className="text-text-dimmed">
|
||||
{children}
|
||||
</Paragraph>
|
||||
) : (
|
||||
children
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
+22
-21
@@ -1,5 +1,4 @@
|
||||
import { BookOpenIcon } from "@heroicons/react/20/solid";
|
||||
import { InformationCircleIcon } from "@heroicons/react/24/outline";
|
||||
import { BookOpenIcon, InformationCircleIcon, LockOpenIcon } from "@heroicons/react/20/solid";
|
||||
import { LoaderFunctionArgs } from "@remix-run/server-runtime";
|
||||
import { typedjson, useTypedLoaderData } from "remix-typedjson";
|
||||
import { AdminDebugTooltip } from "~/components/admin/debugTooltip";
|
||||
@@ -9,7 +8,7 @@ import { PageBody, PageContainer } from "~/components/layout/AppLayout";
|
||||
import { LinkButton } from "~/components/primitives/Buttons";
|
||||
import { ClipboardField } from "~/components/primitives/ClipboardField";
|
||||
import { DateTime } from "~/components/primitives/DateTime";
|
||||
import { Header3 } from "~/components/primitives/Headers";
|
||||
import { InfoPanel } from "~/components/primitives/InfoPanel";
|
||||
import { NavBar, PageAccessories, PageTitle } from "~/components/primitives/PageHeader";
|
||||
import { Paragraph } from "~/components/primitives/Paragraph";
|
||||
import { Property, PropertyTable } from "~/components/primitives/PropertyTable";
|
||||
@@ -23,10 +22,10 @@ import {
|
||||
TableRow,
|
||||
} from "~/components/primitives/Table";
|
||||
import { TextLink } from "~/components/primitives/TextLink";
|
||||
import { UpgradeCallout } from "~/components/primitives/UpgradeCallout";
|
||||
import { useOrganization } from "~/hooks/useOrganizations";
|
||||
import { ApiKeysPresenter } from "~/presenters/v3/ApiKeysPresenter.server";
|
||||
import { requireUserId } from "~/services/session.server";
|
||||
import { ProjectParamSchema, docsPath } from "~/utils/pathBuilder";
|
||||
import { ProjectParamSchema, docsPath, v3BillingPath } from "~/utils/pathBuilder";
|
||||
|
||||
export const loader = async ({ request, params }: LoaderFunctionArgs) => {
|
||||
const userId = await requireUserId(request);
|
||||
@@ -54,6 +53,7 @@ export const loader = async ({ request, params }: LoaderFunctionArgs) => {
|
||||
|
||||
export default function Page() {
|
||||
const { environments, hasStaging } = useTypedLoaderData<typeof loader>();
|
||||
const organization = useOrganization();
|
||||
|
||||
return (
|
||||
<PageContainer>
|
||||
@@ -124,25 +124,26 @@ export default function Page() {
|
||||
</TableBody>
|
||||
</Table>
|
||||
|
||||
<div className="flex items-start gap-3">
|
||||
<div className="flex max-w-sm flex-col items-start justify-between gap-3 rounded-md border border-grid-bright p-4">
|
||||
<div className="flex w-full items-center justify-between gap-2">
|
||||
<InformationCircleIcon className="size-6 text-text-dimmed" />
|
||||
</div>
|
||||
<div className="flex flex-col gap-1">
|
||||
<Header3 className="text-text-bright">Secret keys</Header3>
|
||||
<Paragraph variant={"small"} className="text-text-dimmed">
|
||||
Secret keys should be used on your server. They give full API access and allow you
|
||||
to <TextLink to={docsPath("v3/triggering")}>trigger tasks</TextLink> from your
|
||||
backend.
|
||||
</Paragraph>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex gap-3">
|
||||
<InfoPanel icon={InformationCircleIcon} title="Secret keys">
|
||||
<Paragraph variant="small">
|
||||
Secret keys should be used on your server. They give full API access and allow you
|
||||
to <TextLink to={docsPath("v3/triggering")}>trigger tasks</TextLink> from your
|
||||
backend.
|
||||
</Paragraph>
|
||||
</InfoPanel>
|
||||
|
||||
{!hasStaging && (
|
||||
<UpgradeCallout title="Unlock a Staging environment">
|
||||
<InfoPanel
|
||||
icon={LockOpenIcon}
|
||||
variant="upgrade"
|
||||
title="Unlock a Staging environment"
|
||||
to={v3BillingPath(organization)}
|
||||
buttonLabel="Upgrade"
|
||||
iconClassName="text-indigo-500"
|
||||
>
|
||||
Upgrade your plan to add a Staging environment.
|
||||
</UpgradeCallout>
|
||||
</InfoPanel>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
+10
-3
@@ -1,6 +1,12 @@
|
||||
import { useForm } from "@conform-to/react";
|
||||
import { parse } from "@conform-to/zod";
|
||||
import { BookOpenIcon, PencilSquareIcon, PlusIcon, TrashIcon } from "@heroicons/react/20/solid";
|
||||
import {
|
||||
BookOpenIcon,
|
||||
InformationCircleIcon,
|
||||
PencilSquareIcon,
|
||||
PlusIcon,
|
||||
TrashIcon,
|
||||
} from "@heroicons/react/20/solid";
|
||||
import { Form, Outlet, useActionData, useNavigation } from "@remix-run/react";
|
||||
import {
|
||||
ActionFunctionArgs,
|
||||
@@ -22,6 +28,7 @@ import { Dialog, DialogContent, DialogHeader, DialogTrigger } from "~/components
|
||||
import { Fieldset } from "~/components/primitives/Fieldset";
|
||||
import { FormButtons } from "~/components/primitives/FormButtons";
|
||||
import { FormError } from "~/components/primitives/FormError";
|
||||
import { InfoPanel } from "~/components/primitives/InfoPanel";
|
||||
import { Input } from "~/components/primitives/Input";
|
||||
import { InputGroup } from "~/components/primitives/InputGroup";
|
||||
import { Label } from "~/components/primitives/Label";
|
||||
@@ -256,10 +263,10 @@ export default function Page() {
|
||||
</TableBody>
|
||||
</Table>
|
||||
|
||||
<Callout variant="info" className="mb-4">
|
||||
<InfoPanel icon={InformationCircleIcon} panelClassName="max-w-md">
|
||||
Dev environment variables specified here will be overridden by ones in your .env file
|
||||
when running locally.
|
||||
</Callout>
|
||||
</InfoPanel>
|
||||
</div>
|
||||
<Outlet />
|
||||
</PageBody>
|
||||
|
||||
Reference in New Issue
Block a user