Refactor OIDC buttons: extract OIDCButton component, inline loops
Address PR review feedback: - Remove OIDCButtons wrapper components that only loop, inline .map() directly in SignInPage and ConnectPage - OIDCButton uses useTranslate() and useSafeContinueUrl() hooks internally instead of receiving hook results as props - Move OIDCButton to local _components folders - Fix eslint import grouping in SignInPage Signed-off-by: Bryan Frimin <bryan@getprobo.com>
This commit is contained in:
@@ -2,8 +2,7 @@ import { formatError, type GraphQLError } from "@probo/helpers";
|
|||||||
import { useTranslate } from "@probo/i18n";
|
import { useTranslate } from "@probo/i18n";
|
||||||
import { Button, Field, useToast } from "@probo/ui";
|
import { Button, Field, useToast } from "@probo/ui";
|
||||||
import type { FormEventHandler } from "react";
|
import type { FormEventHandler } from "react";
|
||||||
import { useMutation, usePreloadedQuery } from "react-relay";
|
import { type PreloadedQuery, useMutation, usePreloadedQuery } from "react-relay";
|
||||||
import type { PreloadedQuery } from "react-relay";
|
|
||||||
import { Link, matchPath, useLocation } from "react-router";
|
import { Link, matchPath, useLocation } from "react-router";
|
||||||
import { graphql } from "relay-runtime";
|
import { graphql } from "relay-runtime";
|
||||||
|
|
||||||
@@ -12,7 +11,7 @@ import type { SignInPageQuery } from "#/__generated__/iam/SignInPageQuery.graphq
|
|||||||
import { useSafeContinueUrl } from "#/hooks/useSafeContinueUrl";
|
import { useSafeContinueUrl } from "#/hooks/useSafeContinueUrl";
|
||||||
|
|
||||||
import { Divider } from "./_components/Divider";
|
import { Divider } from "./_components/Divider";
|
||||||
import { OIDCButtons } from "./_components/OIDCButtons";
|
import { OIDCButton } from "./_components/OIDCButton";
|
||||||
|
|
||||||
const signInMutation = graphql`
|
const signInMutation = graphql`
|
||||||
mutation SignInPageMutation($input: SignInInput!) {
|
mutation SignInPageMutation($input: SignInInput!) {
|
||||||
@@ -27,7 +26,7 @@ const signInMutation = graphql`
|
|||||||
export const signInPageQuery = graphql`
|
export const signInPageQuery = graphql`
|
||||||
query SignInPageQuery {
|
query SignInPageQuery {
|
||||||
oidcProviders {
|
oidcProviders {
|
||||||
...OIDCButtonsFragment
|
...OIDCButtonFragment
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
@@ -140,10 +139,9 @@ export default function SignInPage(props: Props) {
|
|||||||
<div className="mt-6 space-y-4">
|
<div className="mt-6 space-y-4">
|
||||||
<Divider>{__("Or")}</Divider>
|
<Divider>{__("Or")}</Divider>
|
||||||
|
|
||||||
<OIDCButtons
|
{data.oidcProviders.map((providerRef, index) => (
|
||||||
providers={data.oidcProviders}
|
<OIDCButton key={index} providerRef={providerRef} />
|
||||||
safeContinueUrl={safeContinueUrl}
|
))}
|
||||||
/>
|
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
|
|||||||
@@ -4,10 +4,11 @@ import type { ComponentProps } from "react";
|
|||||||
import { useFragment } from "react-relay";
|
import { useFragment } from "react-relay";
|
||||||
import { graphql } from "relay-runtime";
|
import { graphql } from "relay-runtime";
|
||||||
|
|
||||||
import type { OIDCButtonsFragment$key } from "#/__generated__/iam/OIDCButtonsFragment.graphql";
|
import type { OIDCButtonFragment$key } from "#/__generated__/iam/OIDCButtonFragment.graphql";
|
||||||
|
import { useSafeContinueUrl } from "#/hooks/useSafeContinueUrl";
|
||||||
|
|
||||||
const fragment = graphql`
|
const fragment = graphql`
|
||||||
fragment OIDCButtonsFragment on OIDCProviderInfo {
|
fragment OIDCButtonFragment on OIDCProviderInfo {
|
||||||
name
|
name
|
||||||
loginURL
|
loginURL
|
||||||
}
|
}
|
||||||
@@ -21,38 +22,13 @@ const providerIcons: Record<
|
|||||||
microsoft: Microsoft,
|
microsoft: Microsoft,
|
||||||
};
|
};
|
||||||
|
|
||||||
export function OIDCButtons({
|
export function OIDCButton({
|
||||||
providers,
|
providerRef,
|
||||||
safeContinueUrl,
|
|
||||||
}: {
|
}: {
|
||||||
providers: ReadonlyArray<OIDCButtonsFragment$key>;
|
providerRef: OIDCButtonFragment$key;
|
||||||
safeContinueUrl: URL;
|
|
||||||
}) {
|
}) {
|
||||||
const { __ } = useTranslate();
|
const { __ } = useTranslate();
|
||||||
|
const safeContinueUrl = useSafeContinueUrl();
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{providers.map((providerRef, index) => (
|
|
||||||
<OIDCButton
|
|
||||||
key={index}
|
|
||||||
providerRef={providerRef}
|
|
||||||
safeContinueUrl={safeContinueUrl}
|
|
||||||
__={__}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function OIDCButton({
|
|
||||||
providerRef,
|
|
||||||
safeContinueUrl,
|
|
||||||
__,
|
|
||||||
}: {
|
|
||||||
providerRef: OIDCButtonsFragment$key;
|
|
||||||
safeContinueUrl: URL;
|
|
||||||
__: (s: string) => string;
|
|
||||||
}) {
|
|
||||||
const provider = useFragment(fragment, providerRef);
|
const provider = useFragment(fragment, providerRef);
|
||||||
const Icon = providerIcons[provider.name];
|
const Icon = providerIcons[provider.name];
|
||||||
|
|
||||||
@@ -1,14 +1,9 @@
|
|||||||
import type { GraphQLError } from "@probo/helpers";
|
import type { GraphQLError } from "@probo/helpers";
|
||||||
import { usePageTitle } from "@probo/hooks";
|
import { usePageTitle } from "@probo/hooks";
|
||||||
import { useTranslate } from "@probo/i18n";
|
import { useTranslate } from "@probo/i18n";
|
||||||
import { Button, Field, Google, Microsoft, useToast } from "@probo/ui";
|
import { Button, Field, useToast } from "@probo/ui";
|
||||||
import { type ComponentProps, useEffect, useRef, useState } from "react";
|
import { useEffect, useRef, useState } from "react";
|
||||||
import {
|
import { type PreloadedQuery, useMutation, usePreloadedQuery } from "react-relay";
|
||||||
type PreloadedQuery,
|
|
||||||
useFragment,
|
|
||||||
useMutation,
|
|
||||||
usePreloadedQuery,
|
|
||||||
} from "react-relay";
|
|
||||||
import { graphql } from "relay-runtime";
|
import { graphql } from "relay-runtime";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
@@ -17,9 +12,9 @@ import { useSafeContinueUrl } from "#/hooks/useSafeContinueUrl";
|
|||||||
import { getPathPrefix } from "#/utils/pathPrefix";
|
import { getPathPrefix } from "#/utils/pathPrefix";
|
||||||
|
|
||||||
import { Divider } from "./_components/Divider";
|
import { Divider } from "./_components/Divider";
|
||||||
|
import { OIDCButton } from "./_components/OIDCButton";
|
||||||
|
|
||||||
import type { ConnectPageMutation, SendMagicLinkInput } from "./__generated__/ConnectPageMutation.graphql";
|
import type { ConnectPageMutation, SendMagicLinkInput } from "./__generated__/ConnectPageMutation.graphql";
|
||||||
import type { ConnectPageOIDCButtonFragment$key } from "./__generated__/ConnectPageOIDCButtonFragment.graphql";
|
|
||||||
import type { ConnectPageQuery } from "./__generated__/ConnectPageQuery.graphql";
|
import type { ConnectPageQuery } from "./__generated__/ConnectPageQuery.graphql";
|
||||||
|
|
||||||
export const connectPageQuery = graphql`
|
export const connectPageQuery = graphql`
|
||||||
@@ -30,7 +25,7 @@ export const connectPageQuery = graphql`
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
oidcProviders {
|
oidcProviders {
|
||||||
...ConnectPageOIDCButtonFragment
|
...OIDCButtonFragment
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
@@ -43,21 +38,6 @@ const sendMagicLinkMutation = graphql`
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const oidcButtonFragment = graphql`
|
|
||||||
fragment ConnectPageOIDCButtonFragment on OIDCProviderInfo {
|
|
||||||
name
|
|
||||||
loginURL
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
const providerIcons: Record<
|
|
||||||
string,
|
|
||||||
(props: ComponentProps<"svg">) => React.ReactNode
|
|
||||||
> = {
|
|
||||||
google: Google,
|
|
||||||
microsoft: Microsoft,
|
|
||||||
};
|
|
||||||
|
|
||||||
const schema = z.object({
|
const schema = z.object({
|
||||||
email: z.string().email(),
|
email: z.string().email(),
|
||||||
});
|
});
|
||||||
@@ -66,65 +46,6 @@ type FormData = z.infer<typeof schema>;
|
|||||||
|
|
||||||
const timerDurationSeconds = 60;
|
const timerDurationSeconds = 60;
|
||||||
|
|
||||||
function OIDCButtons({
|
|
||||||
providers,
|
|
||||||
safeContinueUrl,
|
|
||||||
}: {
|
|
||||||
providers: ReadonlyArray<ConnectPageOIDCButtonFragment$key>;
|
|
||||||
safeContinueUrl: URL;
|
|
||||||
}) {
|
|
||||||
const { __ } = useTranslate();
|
|
||||||
|
|
||||||
if (providers.length === 0) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{providers.map((providerRef, index) => (
|
|
||||||
<OIDCButton
|
|
||||||
key={index}
|
|
||||||
providerRef={providerRef}
|
|
||||||
safeContinueUrl={safeContinueUrl}
|
|
||||||
__={__}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
<Divider>{__("Or")}</Divider>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function OIDCButton({
|
|
||||||
providerRef,
|
|
||||||
safeContinueUrl,
|
|
||||||
__,
|
|
||||||
}: {
|
|
||||||
providerRef: ConnectPageOIDCButtonFragment$key;
|
|
||||||
safeContinueUrl: URL;
|
|
||||||
__: (s: string) => string;
|
|
||||||
}) {
|
|
||||||
const provider = useFragment(oidcButtonFragment, providerRef);
|
|
||||||
const Icon = providerIcons[provider.name];
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Button
|
|
||||||
variant="secondary"
|
|
||||||
className="w-full h-10"
|
|
||||||
onClick={() => {
|
|
||||||
window.location.href
|
|
||||||
= provider.loginURL
|
|
||||||
+ "?continue="
|
|
||||||
+ encodeURIComponent(safeContinueUrl.toString());
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<span className="flex items-center gap-2">
|
|
||||||
{Icon && <Icon width={18} height={18} />}
|
|
||||||
{__(`Sign in with ${provider.name.charAt(0).toUpperCase() + provider.name.slice(1)}`)}
|
|
||||||
</span>
|
|
||||||
</Button>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function ConnectPage(props: {
|
export function ConnectPage(props: {
|
||||||
queryRef: PreloadedQuery<ConnectPageQuery>;
|
queryRef: PreloadedQuery<ConnectPageQuery>;
|
||||||
}) {
|
}) {
|
||||||
@@ -234,12 +155,14 @@ export function ConnectPage(props: {
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="space-y-4">
|
{oidcProviders.length > 0 && (
|
||||||
<OIDCButtons
|
<div className="space-y-4">
|
||||||
providers={oidcProviders}
|
{oidcProviders.map((providerRef, index) => (
|
||||||
safeContinueUrl={safeContinueUrl}
|
<OIDCButton key={index} providerRef={providerRef} />
|
||||||
/>
|
))}
|
||||||
</div>
|
<Divider>{__("Or")}</Divider>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
<form onSubmit={e => void handleSubmit(e)} className="space-y-6">
|
<form onSubmit={e => void handleSubmit(e)} className="space-y-6">
|
||||||
<Field
|
<Field
|
||||||
|
|||||||
52
apps/trust/src/pages/auth/_components/OIDCButton.tsx
Normal file
52
apps/trust/src/pages/auth/_components/OIDCButton.tsx
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
import { useTranslate } from "@probo/i18n";
|
||||||
|
import { Button, Google, Microsoft } from "@probo/ui";
|
||||||
|
import type { ComponentProps } from "react";
|
||||||
|
import { useFragment } from "react-relay";
|
||||||
|
import { graphql } from "relay-runtime";
|
||||||
|
|
||||||
|
import type { OIDCButtonFragment$key } from "./__generated__/OIDCButtonFragment.graphql";
|
||||||
|
import { useSafeContinueUrl } from "#/hooks/useSafeContinueUrl";
|
||||||
|
|
||||||
|
const fragment = graphql`
|
||||||
|
fragment OIDCButtonFragment on OIDCProviderInfo {
|
||||||
|
name
|
||||||
|
loginURL
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const providerIcons: Record<
|
||||||
|
string,
|
||||||
|
(props: ComponentProps<"svg">) => React.ReactNode
|
||||||
|
> = {
|
||||||
|
google: Google,
|
||||||
|
microsoft: Microsoft,
|
||||||
|
};
|
||||||
|
|
||||||
|
export function OIDCButton({
|
||||||
|
providerRef,
|
||||||
|
}: {
|
||||||
|
providerRef: OIDCButtonFragment$key;
|
||||||
|
}) {
|
||||||
|
const { __ } = useTranslate();
|
||||||
|
const safeContinueUrl = useSafeContinueUrl();
|
||||||
|
const provider = useFragment(fragment, providerRef);
|
||||||
|
const Icon = providerIcons[provider.name];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
variant="secondary"
|
||||||
|
className="w-full h-10"
|
||||||
|
onClick={() => {
|
||||||
|
window.location.href
|
||||||
|
= provider.loginURL
|
||||||
|
+ "?continue="
|
||||||
|
+ encodeURIComponent(safeContinueUrl.toString());
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span className="flex items-center gap-2">
|
||||||
|
{Icon && <Icon width={18} height={18} />}
|
||||||
|
{__(`Sign in with ${provider.name.charAt(0).toUpperCase() + provider.name.slice(1)}`)}
|
||||||
|
</span>
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user