Skip to content

Commit

Permalink
UI Feat: Show OAuth Providers in tile view (#291)
Browse files Browse the repository at this point in the history
* chore: move oauth apps to primary sidebar list
- removes the need for a footer

Signed-off-by: Ryan Hopper-Lowe <[email protected]>

* chore: update Oauth apps header to match other page headers

Signed-off-by: Ryan Hopper-Lowe <[email protected]>

* feat: add google oauth card
- rearrange oauth app display
- add google logo

Signed-off-by: Ryan Hopper-Lowe <[email protected]>

* feat: add microsoft 365 oauth card

Signed-off-by: Ryan Hopper-Lowe <[email protected]>

* UI feat: add slack oauth card

Signed-off-by: Ryan Hopper-Lowe <[email protected]>

* UI feat: add notion oauth app

Signed-off-by: Ryan Hopper-Lowe <[email protected]>

* feat: increase bg lightness in dialogs in dark mode

* fix: add OAuth provider icons

---------

Signed-off-by: Ryan Hopper-Lowe <[email protected]>
  • Loading branch information
ryanhopperlowe authored Oct 24, 2024
1 parent ed9b8ff commit dd346d4
Show file tree
Hide file tree
Showing 19 changed files with 199 additions and 42 deletions.
16 changes: 11 additions & 5 deletions ui/admin/app/components/oauth-apps/OAuthAppList.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { TypographyH3, TypographyP } from "~/components/Typography";
import { TypographyH2, TypographyP } from "~/components/Typography";
import { useOAuthAppList } from "~/hooks/oauthApps/useOAuthApps";

import { OAuthAppTile } from "./OAuthAppTile";
Expand All @@ -7,9 +7,11 @@ export function OAuthAppList() {
const apps = useOAuthAppList();

return (
<div className="space-y-10 w-3/4 mx-auto">
<div className="space-y-10">
<div>
<TypographyH3>Supported OAuth Apps</TypographyH3>
<TypographyH2 className="mb-4">
Supported OAuth Apps
</TypographyH2>

<TypographyP className="!mt-0">
These are the currently supported OAuth apps for Otto. These
Expand All @@ -18,9 +20,13 @@ export function OAuthAppList() {
</TypographyP>
</div>

<div className="grid grid-cols-2 gap-10 lg:grid-cols-3 xl:grid-cols-4">
<div className="grid grid-cols-2 gap-10 xl:grid-cols-3">
{apps.map(({ type }) => (
<OAuthAppTile key={type} type={type} />
<OAuthAppTile
key={type}
type={type}
className="justify-self-center"
/>
))}
</div>
</div>
Expand Down
36 changes: 31 additions & 5 deletions ui/admin/app/components/oauth-apps/OAuthAppTile.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,21 @@
import { OAuthProvider } from "~/lib/model/oauthApps/oauth-helpers";
import { cn } from "~/lib/utils";

import { useTheme } from "~/components/theme";
import { Card } from "~/components/ui/card";
import { useOAuthAppInfo } from "~/hooks/oauthApps/useOAuthApps";

import { OAuthAppDetail } from "./OAuthAppDetail";

export function OAuthAppTile({ type }: { type: OAuthProvider }) {
export function OAuthAppTile({
type,
className,
}: {
type: OAuthProvider;
className?: string;
}) {
const info = useOAuthAppInfo(type);
const { isDark } = useTheme();

if (!info) {
console.error(`OAuth app ${type} not found`);
Expand All @@ -15,15 +24,32 @@ export function OAuthAppTile({ type }: { type: OAuthProvider }) {

const { displayName } = info;

const getSrc = () => {
if (isDark) return info.darkLogo ?? info.logo;
return info.logo;
};

return (
<Card className="relative w-[300px] h-[150px] p-4 flex gap-4 justify-center items-center">
<Card
className={cn(
"self-center relative w-[300px] h-[150px] px-6 flex gap-4 justify-center items-center",
className
)}
>
<img
src={info.logo}
src={getSrc()}
alt={displayName}
className="dark:invert m-4"
className={cn("m-4 aspect-auto", {
"dark:invert": info.invertDark,
})}
/>

<OAuthAppDetail type={type} className="absolute top-2 right-2" />
{!info.disableConfiguration && (
<OAuthAppDetail
type={type}
className="absolute top-2 right-2"
/>
)}
</Card>
);
}
7 changes: 6 additions & 1 deletion ui/admin/app/components/oauth-apps/OAuthAppTypeIcon.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
import { NotionLogoIcon } from "@radix-ui/react-icons";
import { KeyIcon } from "lucide-react";
import { FaGithub } from "react-icons/fa";
import { FaGithub, FaGoogle, FaMicrosoft, FaSlack } from "react-icons/fa";

import { OAuthProvider } from "~/lib/model/oauthApps/oauth-helpers";
import { cn } from "~/lib/utils";

const IconMap = {
[OAuthProvider.GitHub]: FaGithub,
[OAuthProvider.Slack]: FaSlack,
[OAuthProvider.Google]: FaGoogle,
[OAuthProvider.Microsoft365]: FaMicrosoft,
[OAuthProvider.Notion]: NotionLogoIcon,
};

export function OAuthAppTypeIcon({
Expand Down
67 changes: 40 additions & 27 deletions ui/admin/app/components/sidebar/Sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,11 @@ const items = [
url: $path("/users"),
icon: User,
},
{
title: "OAuth Apps",
url: $path("/oauth-apps"),
icon: KeyIcon,
},
];

export function AppSidebar() {
Expand All @@ -80,8 +85,8 @@ export function AppSidebar() {
</SidebarHeader>
<SidebarContent
className={cn(
"bg-background",
state === "collapsed" ? "" : "px-2"
"bg-background transition-all duration-300 ease-in-out",
state === "collapsed" ? "" : "px-2 w-fit"
)}
>
<SidebarGroup>
Expand Down Expand Up @@ -110,31 +115,39 @@ export function AppSidebar() {
</SidebarGroupContent>
</SidebarGroup>
</SidebarContent>
<SidebarFooter
className={cn(
"pb-4 bg-background",
state === "collapsed" ? "" : "px-2"
)}
>
<Popover>
<PopoverTrigger asChild>
<SidebarMenuButton className="w-full flex items-center">
<SettingsIcon className="mr-2" /> Settings
</SidebarMenuButton>
</PopoverTrigger>
<PopoverContent side="right" align="end">
<Button variant="secondary" asChild className="w-full">
<Link
to={$path("/oauth-apps")}
className="flex items-center p-2 hover:bg-accent rounded-md"
>
<KeyIcon className="mr-2 h-4 w-4" />
<span>Manage OAuth Apps</span>
</Link>
</Button>
</PopoverContent>
</Popover>
</SidebarFooter>
</Sidebar>
);
}

// disabling this because this will inevitably be used in the future
// eslint-disable-next-line @typescript-eslint/no-unused-vars
function AppSidebarFooter() {
const { state } = useSidebar();
return (
<SidebarFooter
className={cn(
"pb-4 bg-background",
state === "collapsed" ? "" : "px-2"
)}
>
<Popover>
<PopoverTrigger asChild>
<SidebarMenuButton className="w-full flex items-center">
<SettingsIcon className="mr-2" /> Settings
</SidebarMenuButton>
</PopoverTrigger>
<PopoverContent side="right" align="end">
<Button variant="secondary" asChild className="w-full">
<Link
to={$path("/oauth-apps")}
className="flex items-center p-2 hover:bg-accent rounded-md"
>
<KeyIcon className="mr-2 h-4 w-4" />
<span>Manage OAuth Apps</span>
</Link>
</Button>
</PopoverContent>
</Popover>
</SidebarFooter>
);
}
4 changes: 2 additions & 2 deletions ui/admin/app/components/ui/dialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ const DialogOverlay = React.forwardRef<
<DialogPrimitive.Overlay
ref={ref}
className={cn(
"fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
"fixed inset-0 z-50 bg-black/80 dark:bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
className
)}
{...props}
Expand Down Expand Up @@ -52,7 +52,7 @@ const DialogContent = React.forwardRef<
<DialogPrimitive.Content
ref={ref}
className={cn(
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-dialog text-dialog-foreground p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
className,
classNames.content
)}
Expand Down
10 changes: 9 additions & 1 deletion ui/admin/app/lib/model/oauthApps/index.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,18 @@
import { EntityMeta } from "~/lib/model/primitives";

import { GitHubOAuthApp } from "./github";
import { OAuthAppSpec, OAuthProvider } from "./oauth-helpers";
import { GitHubOAuthApp } from "./providers/github";
import { GoogleOAuthApp } from "./providers/google";
import { Microsoft365OAuthApp } from "./providers/microsoft365";
import { NotionOAuthApp } from "./providers/notion";
import { SlackOAuthApp } from "./providers/slack";

export const OAuthAppSpecMap = {
[OAuthProvider.GitHub]: GitHubOAuthApp,
[OAuthProvider.Google]: GoogleOAuthApp,
[OAuthProvider.Microsoft365]: Microsoft365OAuthApp,
[OAuthProvider.Slack]: SlackOAuthApp,
[OAuthProvider.Notion]: NotionOAuthApp,
} as const;

export type OAuthAppDetail = OAuthAppSpec & {
Expand Down
7 changes: 7 additions & 0 deletions ui/admin/app/lib/model/oauthApps/oauth-helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ import { apiBaseUrl } from "~/lib/routers/apiRoutes";

export const OAuthProvider = {
GitHub: "github",
Google: "google",
Microsoft365: "microsoft365",
Slack: "slack",
Notion: "notion",
} as const;
export type OAuthProvider = (typeof OAuthProvider)[keyof typeof OAuthProvider];

Expand All @@ -23,7 +27,10 @@ export type OAuthAppSpec = {
refName: string;
type: OAuthProvider;
logo: string;
darkLogo?: string;
steps: OAuthFormStep[];
disableConfiguration?: boolean;
invertDark?: boolean;
};

export function getOAuthLinks(type: OAuthProvider) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { z } from "zod";
import { BaseUrl } from "~/lib/routers/baseRouter";
import { assetUrl } from "~/lib/utils";

import { OAuthAppSpec, OAuthFormStep, getOAuthLinks } from "./oauth-helpers";
import { OAuthAppSpec, OAuthFormStep, getOAuthLinks } from "../oauth-helpers";

const schema = z.object({
clientID: z.string().min(1, "Client ID is required"),
Expand Down Expand Up @@ -61,4 +61,5 @@ export const GitHubOAuthApp = {
displayName: "GitHub",
logo: assetUrl("/assets/github_logo.svg"),
steps,
invertDark: true,
} satisfies OAuthAppSpec;
20 changes: 20 additions & 0 deletions ui/admin/app/lib/model/oauthApps/providers/google.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { z } from "zod";

import { assetUrl } from "~/lib/utils";

import { OAuthAppSpec } from "../oauth-helpers";

const schema = z.object({
clientID: z.string().min(1, "Client ID is required"),
clientSecret: z.string().min(1, "Client Secret is required"),
});

export const GoogleOAuthApp = {
schema,
refName: "google",
type: "google",
displayName: "Google",
logo: assetUrl("/assets/google_logo.png"),
steps: [],
disableConfiguration: true,
} satisfies OAuthAppSpec;
20 changes: 20 additions & 0 deletions ui/admin/app/lib/model/oauthApps/providers/microsoft365.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { z } from "zod";

import { assetUrl } from "~/lib/utils";

import { OAuthAppSpec } from "../oauth-helpers";

const schema = z.object({
clientID: z.string().min(1, "Client ID is required"),
clientSecret: z.string().min(1, "Client Secret is required"),
});

export const Microsoft365OAuthApp = {
schema,
refName: "microsoft365",
type: "microsoft365",
displayName: "Microsoft 365",
logo: assetUrl("/assets/office365_logo.svg"),
steps: [],
disableConfiguration: true,
} satisfies OAuthAppSpec;
21 changes: 21 additions & 0 deletions ui/admin/app/lib/model/oauthApps/providers/notion.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { z } from "zod";

import { assetUrl } from "~/lib/utils";

import { OAuthAppSpec } from "../oauth-helpers";

const schema = z.object({
clientID: z.string().min(1, "Client ID is required"),
clientSecret: z.string().min(1, "Client Secret is required"),
});

export const NotionOAuthApp = {
schema,
refName: "notion",
type: "notion",
displayName: "Notion",
logo: assetUrl("/assets/notion_logo.png"),
invertDark: true,
steps: [],
disableConfiguration: true,
} satisfies OAuthAppSpec;
21 changes: 21 additions & 0 deletions ui/admin/app/lib/model/oauthApps/providers/slack.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { z } from "zod";

import { assetUrl } from "~/lib/utils";

import { OAuthAppSpec } from "../oauth-helpers";

const schema = z.object({
clientID: z.string().min(1, "Client ID is required"),
clientSecret: z.string().min(1, "Client Secret is required"),
});

export const SlackOAuthApp = {
schema,
refName: "slack",
type: "slack",
displayName: "Slack",
logo: assetUrl("/assets/slack_logo_light.png"),
darkLogo: assetUrl("/assets/slack_logo_dark.png"),
steps: [],
disableConfiguration: true,
} satisfies OAuthAppSpec;
4 changes: 4 additions & 0 deletions ui/admin/app/tailwind.css
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ body {
--card-foreground: 240 10% 3.9%;
--popover: 0 0% 100%;
--popover-foreground: 240 10% 3.9%;
--dialog: 0 0% 100%;
--dialog-foreground: 240 10% 3.9%;
--primary: 240 5.9% 10%;
--primary-foreground: 0 0% 98%;
--secondary: 240 4.8% 95.9%;
Expand Down Expand Up @@ -72,6 +74,8 @@ body {
--card-foreground: 0 0% 98%;
--popover: 240 10% 3.9%;
--popover-foreground: 0 0% 98%;
--dialog: 240 10% 15%;
--dialog-foreground: 0 0% 98%;
--primary: 0 0% 98%;
--primary-foreground: 240 5.9% 10%;
--secondary: 240 3.7% 15.9%;
Expand Down
Binary file added ui/admin/public/assets/google_logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added ui/admin/public/assets/notion_logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit dd346d4

Please sign in to comment.