Skip to content

Commit

Permalink
fix: Service account ui (#16)
Browse files Browse the repository at this point in the history
  • Loading branch information
adityachoudhari26 authored Sep 5, 2024
1 parent 5dd2ddf commit 91e37be
Show file tree
Hide file tree
Showing 7 changed files with 282 additions and 40 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import type {
TargetProvider,
TargetProviderGoogle,
} from "@ctrlplane/db/schema";
import { useState } from "react";
import { TbDots } from "react-icons/tb";

import {
Expand Down Expand Up @@ -35,6 +36,7 @@ type Provider = TargetProvider & {
export const ProviderActionsDropdown: React.FC<{
provider: Provider;
}> = ({ provider }) => {
const [open, setOpen] = useState(false);
const utils = api.useUtils();
const deleteProvider = api.target.provider.delete.useMutation({
onSuccess: () => utils.target.provider.byWorkspaceId.invalidate(),
Expand All @@ -44,7 +46,7 @@ export const ProviderActionsDropdown: React.FC<{
deleteProvider.mutate({ providerId: provider.id, deleteTargets });

return (
<DropdownMenu>
<DropdownMenu open={open} onOpenChange={setOpen}>
<DropdownMenuTrigger asChild>
<Button variant="ghost" className="h-8 w-8 p-0">
<span className="sr-only">Open menu</span>
Expand All @@ -57,13 +59,14 @@ export const ProviderActionsDropdown: React.FC<{
providerId={provider.id}
name={provider.name}
projectIds={provider.googleConfig.projectIds}
onClose={() => setOpen(false)}
>
<DropdownMenuItem onSelect={(e) => e.preventDefault()}>
Edit
</DropdownMenuItem>
</UpdateGoogleProviderDialog>
)}
<AlertDialog>
<AlertDialog onOpenChange={setOpen}>
<AlertDialogTrigger asChild>
<DropdownMenuItem onSelect={(e) => e.preventDefault()}>
Delete
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
"use client";

import { useState } from "react";
import Link from "next/link";
import { useParams, useRouter } from "next/navigation";
import { zodResolver } from "@hookform/resolvers/zod";
import { useFieldArray, useForm } from "react-hook-form";
import { TbBulb, TbCheck, TbCopy } from "react-icons/tb";
import { useCopyToClipboard } from "react-use";
import { z } from "zod";

import { cn } from "@ctrlplane/ui";
Expand All @@ -25,6 +29,7 @@ import {
FormMessage,
} from "@ctrlplane/ui/form";
import { Input } from "@ctrlplane/ui/input";
import { Label } from "@ctrlplane/ui/label";

import { api } from "~/trpc/react";

Expand All @@ -50,6 +55,16 @@ export const GoogleDialog: React.FC<{ children: React.ReactNode }> = ({
control: form.control,
});

const [isCopied, setIsCopied] = useState(false);
const [, copy] = useCopyToClipboard();
const handleCopy = () => {
copy(workspace.data?.googleServiceAccountEmail ?? "");
setIsCopied(true);
setTimeout(() => {
setIsCopied(false);
}, 1000);
};

const router = useRouter();
const utils = api.useUtils();
const create = api.target.provider.managed.google.create.useMutation();
Expand All @@ -75,8 +90,52 @@ export const GoogleDialog: React.FC<{ children: React.ReactNode }> = ({
Google provider allows you to configure and import GKE clusters
from google.
</DialogDescription>

<div
className="relative mb-4 flex w-fit items-center gap-2 rounded-md bg-neutral-800/50 px-4 py-3 text-muted-foreground"
role="alert"
>
<TbBulb className="h-6 w-6 flex-shrink-0" />
<span className="text-sm">
To use the Google provider, you will need to invite our
service account to your project and configure the necessary
permissions. Read more{" "}
<Link
href="https://docs.ctrlplane.dev/integrations/google-cloud/compute-scanner"
target="_blank"
className="underline"
>
here
</Link>
.
</span>
</div>
</DialogHeader>

<div className="space-y-2">
<Label>Service Account</Label>
<div className="relative flex items-center">
<Input
value={workspace.data?.googleServiceAccountEmail ?? ""}
className="disabled:cursor-default"
disabled
/>
<Button
variant="ghost"
size="icon"
type="button"
onClick={handleCopy}
className="absolute right-2 h-4 w-4 bg-neutral-950 backdrop-blur-sm transition-all hover:bg-neutral-950 focus-visible:ring-0"
>
{isCopied ? (
<TbCheck className="h-4 w-4 bg-neutral-950 text-green-500" />
) : (
<TbCopy className="h-4 w-4" />
)}
</Button>
</div>
</div>

<FormField
control={form.control}
name="name"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
"use client";

import { useState } from "react";
import Link from "next/link";
import { useParams } from "next/navigation";
import { TbX } from "react-icons/tb";
import { TbBulb, TbCheck, TbCopy, TbX } from "react-icons/tb";
import { useCopyToClipboard } from "react-use";

import { cn } from "@ctrlplane/ui";
import { Button } from "@ctrlplane/ui/button";
Expand All @@ -24,6 +28,7 @@ import {
useForm,
} from "@ctrlplane/ui/form";
import { Input } from "@ctrlplane/ui/input";
import { Label } from "@ctrlplane/ui/label";

import { api } from "~/trpc/react";
import { createGoogleSchema } from "./GoogleDialog";
Expand All @@ -32,8 +37,9 @@ export const UpdateGoogleProviderDialog: React.FC<{
providerId: string;
name: string;
projectIds: string[];
onClose?: () => void;
children: React.ReactNode;
}> = ({ providerId, name, projectIds, children }) => {
}> = ({ providerId, name, projectIds, onClose, children }) => {
const { workspaceSlug } = useParams<{ workspaceSlug: string }>();
const workspace = api.workspace.bySlug.useQuery(workspaceSlug);
const form = useForm({
Expand All @@ -53,6 +59,7 @@ export const UpdateGoogleProviderDialog: React.FC<{
});
await utils.target.provider.byWorkspaceId.invalidate();
setOpen(false);
onClose?.();
});

const { fields, append, remove } = useFieldArray({
Expand All @@ -62,12 +69,25 @@ export const UpdateGoogleProviderDialog: React.FC<{

const [open, setOpen] = useState(false);

const [isCopied, setIsCopied] = useState(false);
const [, copy] = useCopyToClipboard();
const handleCopy = () => {
copy(workspace.data?.googleServiceAccountEmail ?? "");
setIsCopied(true);
setTimeout(() => {
setIsCopied(false);
}, 1000);
};

return (
<Dialog
open={open}
onOpenChange={(o) => {
setOpen(o);
if (!o) form.reset();
if (!o) {
form.reset();
onClose?.();
}
}}
>
<DialogTrigger asChild>{children}</DialogTrigger>
Expand All @@ -80,8 +100,52 @@ export const UpdateGoogleProviderDialog: React.FC<{
Google provider allows you to configure and import GKE clusters
from google.
</DialogDescription>

<div
className="relative mb-4 flex w-fit items-center gap-2 rounded-md bg-neutral-800/50 px-4 py-3 text-muted-foreground"
role="alert"
>
<TbBulb className="h-6 w-6 flex-shrink-0" />
<span className="text-sm">
To use the Google provider, you will need to invite our
service account to your project and configure the necessary
permissions. Read more{" "}
<Link
href="https://docs.ctrlplane.dev/integrations/google-cloud/compute-scanner"
className="underline"
target="_blank"
>
here
</Link>
.
</span>
</div>
</DialogHeader>

<div className="space-y-2">
<Label>Service Account</Label>
<div className="relative flex items-center">
<Input
value={workspace.data?.googleServiceAccountEmail ?? ""}
className="disabled:cursor-default"
disabled
/>
<Button
variant="ghost"
size="icon"
type="button"
onClick={handleCopy}
className="absolute right-2 h-4 w-4 bg-neutral-950 backdrop-blur-sm transition-all hover:bg-neutral-950 focus-visible:ring-0"
>
{isCopied ? (
<TbCheck className="h-4 w-4 bg-neutral-950 text-green-500" />
) : (
<TbCopy className="h-4 w-4" />
)}
</Button>
</div>
</div>

<FormField
control={form.control}
name="name"
Expand All @@ -108,15 +172,15 @@ export const UpdateGoogleProviderDialog: React.FC<{
Google Projects
</FormLabel>
<FormControl>
<div className="flex items-center gap-2">
<div className="relative flex items-center">
<Input placeholder="my-gcp-project-id" {...field} />

{fields.length > 1 && (
<Button
type="button"
variant="ghost"
size="icon"
className="h-6 w-6"
className="absolute right-2 h-4 w-4 bg-neutral-950 hover:bg-neutral-950"
onClick={() => remove(index)}
>
<TbX className="h-4 w-4" />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import React from "react";
import Link from "next/link";
import { notFound } from "next/navigation";
import {
SiAmazon,
SiGooglecloud,
Expand All @@ -12,6 +14,7 @@ import { cn } from "@ctrlplane/ui";
import { Button } from "@ctrlplane/ui/button";
import { Card } from "@ctrlplane/ui/card";

import { api } from "~/trpc/server";
import { GoogleDialog } from "./google/GoogleDialog";

const Badge: React.FC<{ className?: string; children?: React.ReactNode }> = ({
Expand Down Expand Up @@ -60,7 +63,11 @@ const TargetProviderBadges: React.FC<{ children: React.ReactNode }> = ({
children,
}) => <div className="space-x-1">{children}</div>;

const TargetProviders: React.FC = () => {
const TargetProviders: React.FC<{ workspaceSlug: string }> = async ({
workspaceSlug,
}) => {
const workspace = await api.workspace.bySlug(workspaceSlug);
if (workspace == null) return notFound();
return (
<div className="h-full overflow-y-auto p-8 pb-24">
<div className="container mx-auto max-w-5xl">
Expand Down Expand Up @@ -105,11 +112,21 @@ const TargetProviders: React.FC = () => {
</TargetProviderContent>

<div>
<GoogleDialog>
{workspace.googleServiceAccountEmail != null ? (
<GoogleDialog>
<Button variant="outline" size="sm" className="w-full">
Configure
</Button>
</GoogleDialog>
) : (
<Button variant="outline" size="sm" className="w-full">
Configure
<Link
href={`/${workspaceSlug}/settings/workspace/integrations/google`}
>
Enable
</Link>
</Button>
</GoogleDialog>
)}
</div>
</TargetProviderCard>

Expand Down Expand Up @@ -212,6 +229,10 @@ const TargetProviders: React.FC = () => {
);
};

export default function TargetProviderIntegrationsPage() {
return <TargetProviders />;
export default function TargetProviderIntegrationsPage({
params,
}: {
params: { workspaceSlug: string };
}) {
return <TargetProviders workspaceSlug={params.workspaceSlug} />;
}
Loading

0 comments on commit 91e37be

Please sign in to comment.