Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove branding option #300

Draft
wants to merge 7 commits into
base: main
Choose a base branch
from
Draft
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
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,10 @@ export default async function EditPage({
{page && isProPlan ? (
<CustomDomainForm
defaultValues={{
customDomain: page?.customDomain,
id: page?.id,
...page,
monitors: page.monitorsToPages.map(
({ monitor }) => monitor.id,
),
}} // to be improved
/>
) : (
Expand Down
46 changes: 29 additions & 17 deletions apps/web/src/app/status-page/[domain]/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,21 @@
import { notFound } from "next/navigation";

import { Shell } from "@/components/dashboard/shell";
import { api } from "@/trpc/server";
import NavigationLink from "./_components/navigation-link";

export default function StatusPageLayout({
children,
}: {
type Props = {
params: { domain: string };
children: React.ReactNode;
}) {
};

export default async function StatusPageLayout({ params, children }: Props) {
if (!params.domain) return notFound();
const page = await api.page.getPageBySlug.query({ slug: params.domain });
if (!page) {
return notFound();
}

return (
<div className="flex min-h-screen w-full flex-col space-y-6 p-4 md:p-8">
<header className="mx-auto w-full max-w-xl">
Expand All @@ -19,19 +29,21 @@ export default function StatusPageLayout({
{children}
</Shell>
</main>
<footer className="z-10">
<p className="text-muted-foreground text-center text-sm">
powered by{" "}
<a
href="https://www.openstatus.dev"
target="_blank"
rel="noreferrer"
className="text-foreground underline underline-offset-4 hover:no-underline"
>
openstatus.dev
</a>
</p>
</footer>
{!page.removeBranding && (
<footer className="z-10">
<p className="text-muted-foreground text-center text-sm">
powered by{" "}
<a
href="https://www.openstatus.dev"
target="_blank"
rel="noreferrer"
className="text-foreground underline underline-offset-4 hover:no-underline"
>
openstatus.dev
</a>
</p>
</footer>
)}
</div>
);
}
47 changes: 39 additions & 8 deletions apps/web/src/components/forms/custom-domain-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import type * as z from "zod";
import { insertPageSchemaWithMonitors } from "@openstatus/db/src/schema";

import { Button } from "@/components/ui/button";
import { Checkbox } from "@/components/ui/checkbox";
import {
Form,
FormControl,
Expand All @@ -26,17 +27,23 @@ import DomainStatusIcon from "../domains/domain-status-icon";
import { LoadingAnimation } from "../loading-animation";
import { InputWithAddons } from "../ui/input-with-addons";

const customDomain = insertPageSchemaWithMonitors.pick({
customDomain: true,
id: true,
});

type Schema = z.infer<typeof customDomain>;
type Schema = z.infer<typeof insertPageSchemaWithMonitors>;

export function CustomDomainForm({ defaultValues }: { defaultValues: Schema }) {
const form = useForm<Schema>({
resolver: zodResolver(customDomain),
defaultValues,
resolver: zodResolver(insertPageSchemaWithMonitors),
defaultValues: {
id: defaultValues?.id || 0,
workspaceId: defaultValues?.workspaceId || 0,
title: defaultValues?.title || "",
description: defaultValues?.description || "",
icon: defaultValues?.icon || "",
slug: defaultValues?.slug || "",
customDomain: defaultValues?.customDomain || "",
removeBranding: defaultValues?.removeBranding || false,
monitors: defaultValues?.monitors ?? [],
workspaceSlug: "",
},
});
const router = useRouter();
const [isPending, startTransition] = useTransition();
Expand All @@ -45,8 +52,14 @@ export function CustomDomainForm({ defaultValues }: { defaultValues: Schema }) {
const { status } = domainStatus || {};

async function onSubmit(data: Schema) {
const shouldUpdateBranding =
Boolean(defaultValues?.removeBranding) !== data.removeBranding;

startTransition(async () => {
try {
if (shouldUpdateBranding) {
await api.page.updatePage.mutate(data);
}
if (defaultValues.id) {
await api.page.addCustomDomain.mutate({
customDomain: data.customDomain,
Expand Down Expand Up @@ -114,6 +127,24 @@ export function CustomDomainForm({ defaultValues }: { defaultValues: Schema }) {
</FormItem>
)}
/>
<FormField
control={form.control}
name="removeBranding"
render={({ field }) => (
<FormItem className="flex gap-2 space-y-0 sm:col-span-4">
<FormControl>
<Checkbox
checked={Boolean(field.value)}
onCheckedChange={(value) => field.onChange(Boolean(value))}
/>
</FormControl>

<div className="space-y-1 leading-none">
<FormLabel className="font-normal">Remove branding</FormLabel>
</div>
</FormItem>
)}
/>
<div className="sm:col-span-full">
<Button className="w-full sm:w-auto">
{!isPending ? "Confirm" : <LoadingAnimation />}
Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/config/plans.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export const plansConfig: Record<Plans, PlanProps> = {
features: [
"20 monitors",
"5 status page",
"custom domain",
"custom domain & remove branding",
"1m, 5m, 10m, 30m, 1h checks",
"5 team members",
],
Expand Down
17 changes: 17 additions & 0 deletions packages/api/src/router/page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,23 @@ export const pageRouter = createTRPCRouter({
.all();
const workspaceIds = result.map((workspace) => workspace.workspaceId);

const currentWorkspace = await opts.ctx.db
.select()
.from(workspace)
.where(eq(workspace.id, opts.input.workspaceId))
.get();
if (!currentWorkspace) return;

if (
currentWorkspace.plan === "free" &&
opts.input.removeBranding === true
) {
throw new TRPCError({
code: "FORBIDDEN",
message: "You can't remove branding with the free plan.",
});
}

const pageToUpdate = await opts.ctx.db
.select()
.from(page)
Expand Down
1 change: 1 addition & 0 deletions packages/db/drizzle/0008_fine_toxin.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ALTER TABLE page ADD `remove_branding` integer DEFAULT false;
Loading