diff --git a/apps/console/src/pages/iam/auth/sign-in/SignInPage.tsx b/apps/console/src/pages/iam/auth/sign-in/SignInPage.tsx
index 909f398e2..ca90e50e4 100644
--- a/apps/console/src/pages/iam/auth/sign-in/SignInPage.tsx
+++ b/apps/console/src/pages/iam/auth/sign-in/SignInPage.tsx
@@ -1,13 +1,33 @@
+import { formatError, type GraphQLError } from "@probo/helpers";
import { useTranslate } from "@probo/i18n";
-import { Button } from "@probo/ui";
-import { Suspense } from "react";
-import { useLazyLoadQuery } from "react-relay";
-import { Link, useLocation } from "react-router";
+import { Button, Field, Google, Microsoft, useToast } from "@probo/ui";
+import { type ComponentProps, type FormEventHandler, Suspense } from "react";
+import { useLazyLoadQuery, useMutation } from "react-relay";
+import { Link, useLocation, matchPath } from "react-router";
import { graphql } from "relay-runtime";
+import type { SignInPageMutation } from "#/__generated__/iam/SignInPageMutation.graphql";
import type { SignInPageQuery } from "#/__generated__/iam/SignInPageQuery.graphql";
import { useSafeContinueUrl } from "#/hooks/useSafeContinueUrl";
+const providerIcons: Record<
+ string,
+ (props: ComponentProps<"svg">) => React.ReactNode
+> = {
+ google: Google,
+ microsoft: Microsoft,
+};
+
+const signInMutation = graphql`
+ mutation SignInPageMutation($input: SignInInput!) {
+ signIn(input: $input) {
+ session {
+ id
+ }
+ }
+ }
+`;
+
const oidcProvidersQuery = graphql`
query SignInPageQuery {
oidcProviders {
@@ -17,6 +37,17 @@ const oidcProvidersQuery = graphql`
}
`;
+function Divider({ children }: { children: React.ReactNode }) {
+ return (
+
+ );
+}
+
function OIDCButtons() {
const { __ } = useTranslate();
const safeContinueUrl = useSafeContinueUrl();
@@ -29,88 +60,158 @@ function OIDCButtons() {
return (
<>
- {data.oidcProviders.map((provider) => (
-
- ))}
+ {data.oidcProviders.map((provider) => {
+ const Icon = providerIcons[provider.name];
+ return (
+
+ );
+ })}
>
);
}
export default function SignInPage() {
const { __ } = useTranslate();
-
+ const { toast } = useToast();
const location = useLocation();
+ const safeContinueUrl = useSafeContinueUrl();
+
+ const [signIn, isSigningIn] =
+ useMutation(signInMutation);
+
+ const handleSubmit: FormEventHandler = (e) => {
+ e.preventDefault();
+ const formData = new FormData(e.currentTarget);
+ const email = (formData.get("email") as string) ?? "";
+ const password = (formData.get("password") as string) ?? "";
+
+ if (!email || !password) return;
+
+ const match = matchPath(
+ {
+ path: "/organizations/:organizationId",
+ caseSensitive: false,
+ end: false,
+ },
+ safeContinueUrl.pathname,
+ );
+
+ signIn({
+ variables: {
+ input: {
+ email,
+ password,
+ organizationId: match?.params.organizationId ?? null,
+ },
+ },
+ onCompleted: (_, error) => {
+ if (error) {
+ toast({
+ title: __("Error"),
+ description: formatError(
+ __("Failed to sign in"),
+ error as GraphQLError,
+ ),
+ variant: "error",
+ });
+ return;
+ }
+
+ window.location.href = safeContinueUrl.href;
+ },
+ onError: (e) => {
+ toast({
+ title: __("Error"),
+ description: e.message,
+ variant: "error",
+ });
+ },
+ });
+ };
return (
-
-
- {__("Login to your account")}
+
+
+ {__("Sign in to your account")}
-
- {__("Choose your login method")}
-
-
+