Use compliance page logos for auth layout
Signed-off-by: Émile Ré <emile@getprobo.com>
This commit is contained in:
@@ -1,17 +1,48 @@
|
||||
import { useSystemTheme } from "@probo/hooks";
|
||||
import { Logo } from "@probo/ui";
|
||||
import type { PropsWithChildren } from "react";
|
||||
import { type PreloadedQuery, usePreloadedQuery } from "react-relay";
|
||||
import { Outlet } from "react-router";
|
||||
import { graphql } from "relay-runtime";
|
||||
|
||||
export function AuthLayout(props: PropsWithChildren) {
|
||||
const { children } = props;
|
||||
import type { AuthLayoutQuery } from "./__generated__/AuthLayoutQuery.graphql";
|
||||
|
||||
export const authLayoutQuery = graphql`
|
||||
query AuthLayoutQuery {
|
||||
currentTrustCenter @required(action: THROW) {
|
||||
logoFileUrl
|
||||
darkLogoFileUrl
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export function AuthLayout(props: { queryRef: PreloadedQuery<AuthLayoutQuery> }) {
|
||||
const { queryRef } = props;
|
||||
|
||||
const { currentTrustCenter: compliancePage } = usePreloadedQuery<AuthLayoutQuery>(authLayoutQuery, queryRef);
|
||||
const theme = useSystemTheme();
|
||||
|
||||
const logoFileUrl = theme === "dark"
|
||||
? compliancePage.darkLogoFileUrl ?? compliancePage.logoFileUrl
|
||||
: compliancePage.logoFileUrl;
|
||||
|
||||
return (
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 min-h-screen text-txt-primary">
|
||||
<div className="bg-level-0 flex flex-col items-center justify-center">
|
||||
<div className="w-full max-w-md px-6">{children}</div>
|
||||
<div className="w-full max-w-md px-6">
|
||||
<Outlet />
|
||||
</div>
|
||||
</div>
|
||||
<div className="hidden lg:flex bg-dialog font-bold flex-col items-center justify-center p-8 text-txt-primary lg:p-10">
|
||||
<div className="flex flex-col items-center justify-center gap-4">
|
||||
<Logo withPicto className="w-[440px]" />
|
||||
{logoFileUrl
|
||||
? (
|
||||
<img
|
||||
alt=""
|
||||
src={logoFileUrl}
|
||||
className="size-110 rounded-2xl"
|
||||
/>
|
||||
)
|
||||
: <Logo withPicto className="w-110" />}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
29
apps/trust/src/pages/auth/AuthLayoutLoader.tsx
Normal file
29
apps/trust/src/pages/auth/AuthLayoutLoader.tsx
Normal file
@@ -0,0 +1,29 @@
|
||||
import { useEffect } from "react";
|
||||
import { useQueryLoader } from "react-relay";
|
||||
|
||||
import { RelayProvider } from "#/providers/RelayProviders";
|
||||
|
||||
import type { AuthLayoutQuery } from "./__generated__/AuthLayoutQuery.graphql";
|
||||
import { AuthLayout, authLayoutQuery } from "./AuthLayout";
|
||||
|
||||
function AuthLayoutQueryLoader() {
|
||||
const [queryRef, loadQuery] = useQueryLoader<AuthLayoutQuery>(authLayoutQuery);
|
||||
|
||||
useEffect(() => {
|
||||
if (!queryRef) {
|
||||
return loadQuery({});
|
||||
}
|
||||
});
|
||||
|
||||
if (!queryRef) return null;
|
||||
|
||||
return <AuthLayout queryRef={queryRef} />;
|
||||
}
|
||||
|
||||
export default function AuthLayoutLoader() {
|
||||
return (
|
||||
<RelayProvider>
|
||||
<AuthLayoutQueryLoader />
|
||||
</RelayProvider>
|
||||
);
|
||||
}
|
||||
@@ -14,7 +14,6 @@ import { useFormWithSchema } from "#/hooks/useFormWithSchema";
|
||||
|
||||
import type { ConnectPageMutation } from "./__generated__/ConnectPageMutation.graphql";
|
||||
import type { ConnectPageQuery } from "./__generated__/ConnectPageQuery.graphql";
|
||||
import { AuthLayout } from "./AuthLayout";
|
||||
|
||||
export const connectPageQuery = graphql`
|
||||
query ConnectPageQuery {
|
||||
@@ -126,49 +125,47 @@ export function ConnectPage(props: {
|
||||
});
|
||||
|
||||
return (
|
||||
<AuthLayout>
|
||||
<div className="space-y-6 w-full max-w-md mx-auto">
|
||||
<div className="space-y-2 text-center">
|
||||
<h1 className="text-3xl font-bold">
|
||||
{__(`Connect to ${organization.name}'s compliance page`)}
|
||||
</h1>
|
||||
<p className="text-txt-tertiary">
|
||||
<div className="space-y-6 w-full max-w-md mx-auto">
|
||||
<div className="space-y-2 text-center">
|
||||
<h1 className="text-3xl font-bold">
|
||||
{__(`Connect to ${organization.name}'s compliance page`)}
|
||||
</h1>
|
||||
<p className="text-txt-tertiary">
|
||||
{__(
|
||||
"Enter your email address to connect with a magic link and start requesting access to documents",
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<form onSubmit={e => void handleSubmit(e)} className="space-y-4">
|
||||
<Field
|
||||
label={__("Email")}
|
||||
placeholder="john.doe@acme.com"
|
||||
{...register("email")}
|
||||
type="email"
|
||||
error={formState.errors.email?.message}
|
||||
/>
|
||||
|
||||
{magicLinkSent && (
|
||||
<p className="text-txt-primary text-sm">
|
||||
{__(
|
||||
"Enter your email address to connect with a magic link and start requesting access to documents",
|
||||
"Magic Link Sent! Check your emails and use the link to connect.",
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<form onSubmit={e => void handleSubmit(e)} className="space-y-4">
|
||||
<Field
|
||||
label={__("Email")}
|
||||
placeholder="john.doe@acme.com"
|
||||
{...register("email")}
|
||||
type="email"
|
||||
error={formState.errors.email?.message}
|
||||
/>
|
||||
|
||||
{magicLinkSent && (
|
||||
<p className="text-txt-primary text-sm">
|
||||
{__(
|
||||
"Magic Link Sent! Check your emails and use the link to connect.",
|
||||
)}
|
||||
</p>
|
||||
)}
|
||||
|
||||
<Button
|
||||
type="submit"
|
||||
className="w-full"
|
||||
disabled={formState.isSubmitting || (magicLinkSent && timer !== 0)}
|
||||
>
|
||||
{magicLinkSent
|
||||
? timer === 0
|
||||
? __("Resend Link")
|
||||
: `${__("Resend Link in")} ${timer}s`
|
||||
: __("Send Magic Link")}
|
||||
</Button>
|
||||
</form>
|
||||
</div>
|
||||
</AuthLayout>
|
||||
<Button
|
||||
type="submit"
|
||||
className="w-full"
|
||||
disabled={formState.isSubmitting || (magicLinkSent && timer !== 0)}
|
||||
>
|
||||
{magicLinkSent
|
||||
? timer === 0
|
||||
? __("Resend Link")
|
||||
: `${__("Resend Link in")} ${timer}s`
|
||||
: __("Send Magic Link")}
|
||||
</Button>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -12,7 +12,6 @@ import { useFormWithSchema } from "#/hooks/useFormWithSchema";
|
||||
import { getPathPrefix } from "#/utils/pathPrefix";
|
||||
|
||||
import type { VerifyMagicLinkPageMutation } from "./__generated__/VerifyMagicLinkPageMutation.graphql";
|
||||
import { AuthLayout } from "./AuthLayout";
|
||||
|
||||
const verifyMagicLinkMutation = graphql`
|
||||
mutation VerifyMagicLinkPageMutation($input: VerifyMagicLinkInput!) {
|
||||
@@ -87,39 +86,37 @@ export default function VerifyMagicLinkPagePageMutation() {
|
||||
});
|
||||
|
||||
return (
|
||||
<AuthLayout>
|
||||
<div className="space-y-6 w-full max-w-md mx-auto">
|
||||
<div className="space-y-2 text-center">
|
||||
<h1 className="text-3xl font-bold">{__("Email Confirmation")}</h1>
|
||||
<p className="text-txt-tertiary">
|
||||
{__("Confirm your email address to complete registration")}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<form onSubmit={e => void handleSubmit(e)} className="space-y-4">
|
||||
<Field
|
||||
label={__("Confirmation Token")}
|
||||
type="text"
|
||||
placeholder={__("Enter your confirmation token")}
|
||||
{...form.register("token")}
|
||||
error={form.formState.errors.token?.message}
|
||||
disabled={form.formState.isSubmitting}
|
||||
help={__(
|
||||
"The token has been automatically filled from the URL if available",
|
||||
)}
|
||||
/>
|
||||
|
||||
<Button
|
||||
type="submit"
|
||||
className="w-full"
|
||||
disabled={form.formState.isSubmitting}
|
||||
>
|
||||
{form.formState.isSubmitting
|
||||
? __("Confirming...")
|
||||
: __("Confirm Email")}
|
||||
</Button>
|
||||
</form>
|
||||
<div className="space-y-6 w-full max-w-md mx-auto">
|
||||
<div className="space-y-2 text-center">
|
||||
<h1 className="text-3xl font-bold">{__("Email Confirmation")}</h1>
|
||||
<p className="text-txt-tertiary">
|
||||
{__("Confirm your email address to complete registration")}
|
||||
</p>
|
||||
</div>
|
||||
</AuthLayout>
|
||||
|
||||
<form onSubmit={e => void handleSubmit(e)} className="space-y-4">
|
||||
<Field
|
||||
label={__("Confirmation Token")}
|
||||
type="text"
|
||||
placeholder={__("Enter your confirmation token")}
|
||||
{...form.register("token")}
|
||||
error={form.formState.errors.token?.message}
|
||||
disabled={form.formState.isSubmitting}
|
||||
help={__(
|
||||
"The token has been automatically filled from the URL if available",
|
||||
)}
|
||||
/>
|
||||
|
||||
<Button
|
||||
type="submit"
|
||||
className="w-full"
|
||||
disabled={form.formState.isSubmitting}
|
||||
>
|
||||
{form.formState.isSubmitting
|
||||
? __("Confirming...")
|
||||
: __("Confirm Email")}
|
||||
</Button>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
108
apps/trust/src/pages/auth/__generated__/AuthLayoutQuery.graphql.ts
generated
Normal file
108
apps/trust/src/pages/auth/__generated__/AuthLayoutQuery.graphql.ts
generated
Normal file
@@ -0,0 +1,108 @@
|
||||
/**
|
||||
* @generated SignedSource<<c3e41c1d38cd6923e718ea03bba5bb44>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
import { ConcreteRequest } from 'relay-runtime';
|
||||
export type AuthLayoutQuery$variables = Record<PropertyKey, never>;
|
||||
export type AuthLayoutQuery$data = {
|
||||
readonly currentTrustCenter: {
|
||||
readonly darkLogoFileUrl: string | null | undefined;
|
||||
readonly logoFileUrl: string | null | undefined;
|
||||
};
|
||||
};
|
||||
export type AuthLayoutQuery = {
|
||||
response: AuthLayoutQuery$data;
|
||||
variables: AuthLayoutQuery$variables;
|
||||
};
|
||||
|
||||
const node: ConcreteRequest = (function(){
|
||||
var v0 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "logoFileUrl",
|
||||
"storageKey": null
|
||||
},
|
||||
v1 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "darkLogoFileUrl",
|
||||
"storageKey": null
|
||||
};
|
||||
return {
|
||||
"fragment": {
|
||||
"argumentDefinitions": [],
|
||||
"kind": "Fragment",
|
||||
"metadata": null,
|
||||
"name": "AuthLayoutQuery",
|
||||
"selections": [
|
||||
{
|
||||
"kind": "RequiredField",
|
||||
"field": {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "TrustCenter",
|
||||
"kind": "LinkedField",
|
||||
"name": "currentTrustCenter",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v0/*: any*/),
|
||||
(v1/*: any*/)
|
||||
],
|
||||
"storageKey": null
|
||||
},
|
||||
"action": "THROW"
|
||||
}
|
||||
],
|
||||
"type": "Query",
|
||||
"abstractKey": null
|
||||
},
|
||||
"kind": "Request",
|
||||
"operation": {
|
||||
"argumentDefinitions": [],
|
||||
"kind": "Operation",
|
||||
"name": "AuthLayoutQuery",
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "TrustCenter",
|
||||
"kind": "LinkedField",
|
||||
"name": "currentTrustCenter",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v0/*: any*/),
|
||||
(v1/*: any*/),
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "id",
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
]
|
||||
},
|
||||
"params": {
|
||||
"cacheID": "92bbf193e99aa98673d1926bd1fdc890",
|
||||
"id": null,
|
||||
"metadata": {},
|
||||
"name": "AuthLayoutQuery",
|
||||
"operationKind": "query",
|
||||
"text": "query AuthLayoutQuery {\n currentTrustCenter {\n logoFileUrl\n darkLogoFileUrl\n id\n }\n}\n"
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
(node as any).hash = "d76b10f9b919f4224c4c8b478e66b4f1";
|
||||
|
||||
export default node;
|
||||
@@ -1,10 +1,11 @@
|
||||
import { lazy } from "@probo/react-lazy";
|
||||
import {
|
||||
type AppRoute,
|
||||
loaderFromQueryLoader,
|
||||
routeFromAppRoute,
|
||||
withQueryRef,
|
||||
} from "@probo/routes";
|
||||
import { Fragment, lazy } from "react";
|
||||
import { Fragment } from "react";
|
||||
import { loadQuery } from "react-relay";
|
||||
import { createBrowserRouter, redirect, useRouteError } from "react-router";
|
||||
|
||||
@@ -34,12 +35,17 @@ function ErrorBoundary() {
|
||||
|
||||
const routes = [
|
||||
{
|
||||
path: "/connect",
|
||||
Component: lazy(() => import("./pages/auth/ConnectPageLoader")),
|
||||
},
|
||||
{
|
||||
path: "/verify-magic-link",
|
||||
Component: lazy(() => import("./pages/auth/VerifyMagicLinkPage")),
|
||||
Component: lazy(() => import("#/pages/auth/AuthLayoutLoader")),
|
||||
children: [
|
||||
{
|
||||
path: "/connect",
|
||||
Component: lazy(() => import("#/pages/auth/ConnectPageLoader")),
|
||||
},
|
||||
{
|
||||
path: "/verify-magic-link",
|
||||
Component: lazy(() => import("#/pages/auth/VerifyMagicLinkPage")),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: "/",
|
||||
|
||||
Reference in New Issue
Block a user