-
-
-
-
-
-
+ <>
+
+ {!userInitialized ?
+ (
+
+
+
+
+
+ Login
+ Create Account
+
+
+
+
+
+
+
+
+
+
+
+
-
+ ) :
+ (
+ <>>
+ )
+ }
+ >
)
}
diff --git a/apps/client-ts/src/app/b2c/profile/layout.tsx b/apps/client-ts/src/app/b2c/profile/layout.tsx
deleted file mode 100644
index d08ecad2d..000000000
--- a/apps/client-ts/src/app/b2c/profile/layout.tsx
+++ /dev/null
@@ -1,28 +0,0 @@
-'use client'
-import "./../../globals.css";
-import { RootLayout } from "@/components/RootLayout";
-import { useStytchSession } from "@stytch/nextjs";
-import { useRouter } from "next/navigation";
-import { useEffect } from "react";
-
-
-export default function Layout({
- children,
-}: Readonly<{
- children: React.ReactNode;
-}>) {
- const { session,isInitialized } = useStytchSession();
- const router = useRouter();
- useEffect(() => {
- if (isInitialized && !session) {
- router.replace("/b2c/login");
- }
- }, [session, isInitialized, router]);
- //console.log('WEBAPP DOMAIN is '+ process.env.NEXT_PUBLIC_WEBAPP_DOMAIN)
- return (
- <>
-
- {children}
- >
- );
-}
diff --git a/apps/client-ts/src/app/configuration/layout.tsx b/apps/client-ts/src/app/configuration/layout.tsx
deleted file mode 100644
index 38ce8043d..000000000
--- a/apps/client-ts/src/app/configuration/layout.tsx
+++ /dev/null
@@ -1,30 +0,0 @@
-'use client'
-import { Inter } from "next/font/google";
-import "./../globals.css";
-import { RootLayout } from "@/components/RootLayout";
-import { useStytchSession } from "@stytch/nextjs";
-import { useRouter } from "next/navigation";
-import { useEffect } from "react";
-import config from "@/lib/config";
-
-export default function Layout({
- children,
-}: Readonly<{
- children: React.ReactNode;
-}>) {
- const { session,isInitialized } = useStytchSession();
-
- const router = useRouter();
- useEffect(() => {
- if(config.DISTRIBUTION !== "selfhosted" && isInitialized && !session){
- router.push("/b2c/login");
- }
- }, [session, isInitialized, router]);
-
- return (
- <>
-
- {children}
- >
- );
-}
diff --git a/apps/client-ts/src/app/connections/layout.tsx b/apps/client-ts/src/app/connections/layout.tsx
deleted file mode 100644
index 1ff2e6a2f..000000000
--- a/apps/client-ts/src/app/connections/layout.tsx
+++ /dev/null
@@ -1,31 +0,0 @@
-'use client'
-import { Inter } from "next/font/google";
-import "./../globals.css";
-import { RootLayout } from "@/components/RootLayout";
-import { useStytchSession } from "@stytch/nextjs";
-import { useRouter } from "next/navigation";
-import { useEffect } from "react";
-import config from "@/lib/config";
-
-const inter = Inter({ subsets: ["latin"] });
-
-export default function Layout({
- children,
-}: Readonly<{
- children: React.ReactNode;
-}>) {
- const { session,isInitialized } = useStytchSession();
- const router = useRouter();
- useEffect(() => {
- if(config.DISTRIBUTION !== "selfhosted" && isInitialized && !session){
- router.push("/b2c/login");
- }
- }, [session,isInitialized, router]);
-
- return (
- <>
-
- {children}
- >
- );
-}
diff --git a/apps/client-ts/src/app/dashboard/layout.tsx b/apps/client-ts/src/app/dashboard/layout.tsx
deleted file mode 100644
index 3d766efc6..000000000
--- a/apps/client-ts/src/app/dashboard/layout.tsx
+++ /dev/null
@@ -1,31 +0,0 @@
-'use client'
-import { Inter } from "next/font/google";
-import "./../globals.css";
-import { RootLayout } from "@/components/RootLayout";
-import { useStytchSession } from "@stytch/nextjs";
-import { useRouter } from "next/navigation";
-import { useEffect } from "react";
-import config from "@/lib/config";
-
-const inter = Inter({ subsets: ["latin"] });
-
-export default function Layout({
- children,
-}: Readonly<{
- children: React.ReactNode;
-}>) {
- const { session,isInitialized } = useStytchSession();
- const router = useRouter();
- useEffect(() => {
- if(config.DISTRIBUTION !== "selfhosted" && isInitialized && !session){
- router.replace("/b2c/login");
- }
- }, [session,isInitialized, router]);
-
- return (
- <>
-
- {children}
- >
- );
-}
diff --git a/apps/client-ts/src/app/events/layout.tsx b/apps/client-ts/src/app/events/layout.tsx
deleted file mode 100644
index 6916486b2..000000000
--- a/apps/client-ts/src/app/events/layout.tsx
+++ /dev/null
@@ -1,32 +0,0 @@
-'use client'
-import { Inter } from "next/font/google";
-import "./../globals.css";
-import { RootLayout } from "@/components/RootLayout";
-import { useStytchSession } from "@stytch/nextjs";
-import { useRouter } from "next/navigation";
-import { useEffect } from "react";
-import config from "@/lib/config";
-
-const inter = Inter({ subsets: ["latin"] });
-
-export default function Layout({
- children,
-}: Readonly<{
- children: React.ReactNode;
-}>) {
- const { session, isInitialized} = useStytchSession();
- const router = useRouter();
- useEffect(() => {
-
- if(config.DISTRIBUTION !== "selfhosted" && isInitialized && !session){
- router.replace("/b2c/login");
- }
- }, [session,isInitialized, router]);
-
- return (
- <>
-
- {children}
- >
- );
-}
diff --git a/apps/client-ts/src/app/layout.tsx b/apps/client-ts/src/app/layout.tsx
index 865a96b3e..f1ba7bb41 100644
--- a/apps/client-ts/src/app/layout.tsx
+++ b/apps/client-ts/src/app/layout.tsx
@@ -3,6 +3,7 @@ import { Inter } from "next/font/google";
import "./globals.css";
import { Provider } from "@/components/Provider/provider";
import { Toaster } from "@/components/ui/sonner"
+import {ThemeProvider} from '@/components/Nav/theme-provider'
const inter = Inter({ subsets: ["latin"] });
@@ -17,12 +18,20 @@ export default function RootLayout({
children: React.ReactNode;
}>) {
return (
-
-
-
- {children}
-
-
+
+
+
+
+ {children}
+
+
+
+
);
diff --git a/apps/client-ts/src/components/Auth/CustomLoginComponent/CreateUserForm.tsx b/apps/client-ts/src/components/Auth/CustomLoginComponent/CreateUserForm.tsx
new file mode 100644
index 000000000..314114c9b
--- /dev/null
+++ b/apps/client-ts/src/components/Auth/CustomLoginComponent/CreateUserForm.tsx
@@ -0,0 +1,165 @@
+"use client"
+import React from 'react'
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardFooter,
+ CardHeader,
+ CardTitle,
+ } from "@/components/ui/card"
+import { Input } from "@/components/ui/input"
+import { Button } from "@/components/ui/button"
+import { Label } from "@/components/ui/label"
+import * as z from "zod"
+import {
+ Form,
+ FormControl,
+ FormDescription,
+ FormField,
+ FormItem,
+ FormLabel,
+ FormMessage,
+} from "@/components/ui/form"
+import { zodResolver } from "@hookform/resolvers/zod"
+import { useForm } from "react-hook-form"
+import { PasswordInput } from '@/components/ui/password-input'
+import useCreateUserMutation from '@/hooks/mutations/useCreateUserMutation'
+
+const formSchema = z.object({
+ first_name: z.string().min(2,{
+ message:"Enter First Name"
+ }),
+ last_name : z.string().min(2, {
+ message: "Enter Last Name.",
+ }),
+ email : z.string().email({
+ message: "Enter valid Email.",
+ }),
+ password : z.string().min(2, {
+ message: "Enter Password.",
+ }),
+
+})
+
+const CreateUserForm = () => {
+
+ const {mutate : createUserMutate} = useCreateUserMutation();
+
+ const form = useForm
>({
+ resolver: zodResolver(formSchema),
+ defaultValues: {
+ first_name:'',
+ last_name:'',
+ email:'',
+ password:''
+ },
+ })
+
+ const onSubmit = (values: z.infer) => {
+ // console.log(values)
+ createUserMutate({
+ first_name:values.first_name,
+ last_name:values.last_name,
+ email:values.email,
+ strategy:'b2c',
+ password_hash:values.password
+ },
+ {
+ onSuccess:() => {
+ form.reset();
+ }
+ });
+
+
+ }
+
+
+ return (
+ <>
+
+
+ >
+ )
+}
+
+export default CreateUserForm
\ No newline at end of file
diff --git a/apps/client-ts/src/components/Auth/CustomLoginComponent/LoginUserForm.tsx b/apps/client-ts/src/components/Auth/CustomLoginComponent/LoginUserForm.tsx
new file mode 100644
index 000000000..5726493bc
--- /dev/null
+++ b/apps/client-ts/src/components/Auth/CustomLoginComponent/LoginUserForm.tsx
@@ -0,0 +1,122 @@
+"use client"
+import React from 'react'
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardFooter,
+ CardHeader,
+ CardTitle,
+ } from "@/components/ui/card"
+import { Input } from "@/components/ui/input"
+import { Button } from "@/components/ui/button"
+import { Label } from "@/components/ui/label"
+import { PasswordInput } from '@/components/ui/password-input'
+import * as z from "zod"
+import {
+ Form,
+ FormControl,
+ FormDescription,
+ FormField,
+ FormItem,
+ FormLabel,
+ FormMessage,
+} from "@/components/ui/form"
+import { zodResolver } from "@hookform/resolvers/zod"
+import { useForm } from "react-hook-form"
+import useLoginMutation from '@/hooks/mutations/useLoginMutation'
+import { useRouter } from "next/navigation";
+
+const formSchema = z.object({
+ email: z.string().email({
+ message:"Enter valid Email"
+ }),
+ password : z.string().min(2, {
+ message: "Enter Password.",
+ }),
+})
+
+const LoginUserForm = () => {
+
+ const router = useRouter()
+
+ const {mutate : loginMutate} = useLoginMutation()
+
+ const form = useForm>({
+ resolver: zodResolver(formSchema),
+ defaultValues: {
+ email:'',
+ password:''
+ },
+ })
+
+const onSubmit = (values: z.infer) => {
+
+ loginMutate({
+ email:values.email,
+ password_hash:values.password
+ },
+ {
+ onSuccess: () => router.replace("/connections")
+ })
+
+}
+
+
+
+ return (
+ <>
+
+
+ >
+ )
+}
+
+export default LoginUserForm
\ No newline at end of file
diff --git a/apps/client-ts/src/components/Auth/DashboardClient.tsx b/apps/client-ts/src/components/Auth/DashboardClient.tsx
deleted file mode 100644
index 095366fd7..000000000
--- a/apps/client-ts/src/components/Auth/DashboardClient.tsx
+++ /dev/null
@@ -1,386 +0,0 @@
-'use client'
-
-import {
- FormEventHandler,
- MouseEventHandler,
- useEffect,
- useState,
-} from "react";
-import { usePathname, useRouter, useSearchParams } from "next/navigation";
-import Link from "next/link";
-import { Input } from "@/components/ui/input"
-
-import {
- createOidcSSOConn,
- createSamlSSOConn,
- deleteMember,
- invite,
-} from "@/lib/stytch/api";
-import {
- Member,
- OIDCConnection,
- Organization,
- SAMLConnection,
-} from "@/lib/stytch/loadStytch";
-import {
- Avatar,
- AvatarFallback,
- AvatarImage,
-} from "@/components/ui/avatar"
-import { Button } from "@/components/ui/button"
-import {
- Card,
- CardContent,
- CardDescription,
- CardHeader,
- CardTitle,
-} from "@/components/ui/card"
-
-import { Separator } from "@/components/ui/separator"
-import { CircleIcon } from "@radix-ui/react-icons"
-import { useCreateSamlSso } from "@/hooks/stytch/useCreateSamlSso";
-import { useCreateOidcSso } from "@/hooks/stytch/useCreateOidcSso";
-import { useInvite } from "@/hooks/stytch/useInvite";
-import { useDeleteMember } from "@/hooks/stytch/useDeleteMember";
-
-
-type Props = {
- org: Organization;
- user: Member;
- members: Member[];
- saml_connections: SAMLConnection[];
- oidc_connections: OIDCConnection[];
-};
-
-const isValidEmail = (emailValue: string) => {
- // Overly simple email address regex
- const regex = /\S+@\S+\.\S+/;
- return regex.test(emailValue);
-};
-
-const isAdmin = (member: Member) => !!member.trusted_metadata!.admin;
-
-const SSO_METHOD = {
- SAML: "SAML",
- OIDC: "OIDC",
-};
-
-const MemberRow = ({ member, user }: { member: Member; user: Member; }) => {
- const router = useRouter();
- const pathname = usePathname();
- const [isDisabled, setIsDisabled] = useState(false);
-
- const { mutate, isLoading, error, data } = useDeleteMember();
-
- const doDelete: MouseEventHandler = (e) => {
- e.preventDefault();
- setIsDisabled(true);
- mutate(member.member_id);
- // Force a reload to refresh the user list
- router.replace(pathname);
- // TODO: Success toast?
- };
-
- const canDelete =
- /* Do not let members delete themselves! */
- member.member_id !== user.member_id &&
- /* Only admins can delete! */
- isAdmin(user);
-
- const deleteButton = (
-
- );
-
- return (
-
-
-
-
- OM
-
-
-
- {member.email_address} (
- {member.status})
-
-
{isAdmin(member) ? "@admin" : "@member"}
-
-
-
- {canDelete ? deleteButton : null}
-
- );
-};
-
-const MemberList = ({
- members,
- user,
- org,
-}: Pick) => {
- const router = useRouter();
- const pathname = usePathname();
- const [email, setEmail] = useState("");
- const [isDisabled, setIsDisabled] = useState(true);
-
- useEffect(() => {
- setIsDisabled(!isValidEmail(email));
- }, [email]);
-
- const { mutate, isLoading, error, data } = useInvite();
-
-
- const onInviteSubmit: FormEventHandler = (e) => {
- e.preventDefault();
- // Disable button right away to prevent sending emails twice
- if (isDisabled) {
- return;
- } else {
- setIsDisabled(true);
- }
- mutate(email);
- // Force a reload to refresh the user list
- router.replace(pathname);
- };
-
- return (
- <>
-
-
People with access
-
- {members.map((member) => (
-
- ))}
-
-
-
-
-
Invite new member
-
-
- >
- );
-};
-
-const IDPList = ({
- user,
- saml_connections,
- oidc_connections,
-}: Pick) => {
- const [idpNameSAML, setIdpNameSAML] = useState("");
- const [idpNameOIDC, setIdpNameOIDC] = useState("");
- const [ssoMethod, setSsoMethod] = useState(SSO_METHOD.SAML);
- const router = useRouter();
- const searchParams = useSearchParams();
-
- const { mutate: samlMutate, isLoading: samlLoading, error: samlError, data: samlData } = useCreateSamlSso();
- const { mutate: oidcMutate, isLoading: oidcLoading, error: oidcError, data: oidcData } = useCreateOidcSso();
-
-
- const onSamlCreate: FormEventHandler = (e) => {
- e.preventDefault();
- samlMutate(idpNameSAML);
- if (samlError) {
- alert("Error creating connection");
- return;
- }
- const conn = samlData as any;
- router.push(
- `/${searchParams.get('slug')}/dashboard/saml/${conn.connection_id}`
- );
- };
-
- const onOidcCreate: FormEventHandler = (e) => {
- e.preventDefault();
- oidcMutate(idpNameOIDC);
- if (oidcError) {
- alert("Error creating connection");
- return;
- }
- const conn = oidcData as any;
- router.push(
- `/${searchParams.get('slug')}/dashboard/oidc/${conn.connection_id}`
- );
- };
-
- return (
- <>
-
- <>
-
SSO Connections
-
SAML
- {saml_connections.length === 0 &&
No connections configured.
}
-
- {saml_connections.map((conn) => (
- -
-
-
- {conn!.display_name} ({conn!.status})
-
-
-
- ))}
-
-
OIDC
- {oidc_connections.length === 0 &&
No connections configured.
}
-
- {oidc_connections.map((conn) => (
- -
-
-
- {conn!.display_name} ({conn!.status})
-
-
-
- ))}
-
- >
-
-
- {/*Only admins can create new SSO Connection*/}
- {isAdmin(user) && (
-
- )}
- >
- );
-};
-
-{/*
-
- MFA Setting: {org.mfa_policy}
-
-
-
-
-*/}
-
-const DashboardClient = ({
- org,
- user,
- members,
- saml_connections,
- oidc_connections,
-}: {
- org: Organization,
- user: Member,
- members: Member[],
- saml_connections: SAMLConnection[],
- oidc_connections: OIDCConnection[]
-}) => {
-
- return (
-
-
-
- Organization
-
-
-
- {org.organization_name}
-
-
-
-
- Connected user
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- );
-};
-
-
-export default DashboardClient;
diff --git a/apps/client-ts/src/components/Auth/DiscoveryClient.tsx b/apps/client-ts/src/components/Auth/DiscoveryClient.tsx
deleted file mode 100644
index d864119c2..000000000
--- a/apps/client-ts/src/components/Auth/DiscoveryClient.tsx
+++ /dev/null
@@ -1,53 +0,0 @@
-'use client';
-
-import { useState } from "react";
-
-/*const CreateNewOrganization = () => {
- const [orgName, setOrgName] = useState("");
- const [requireMFA, setRequireMFA] = useState(false);
- return (
-
-
Or, create a new Organization
-
-
-
- );
-};*/
-
-
-const DiscoveryClient = ({
- children,
-}: {
- children: React.ReactNode
-}) => {
- return (
-
- {children}
- {/**/}
-
- );
-};
-
-export default DiscoveryClient;
diff --git a/apps/client-ts/src/components/Auth/DiscoveryServer.tsx b/apps/client-ts/src/components/Auth/DiscoveryServer.tsx
deleted file mode 100644
index 887b72eef..000000000
--- a/apps/client-ts/src/components/Auth/DiscoveryServer.tsx
+++ /dev/null
@@ -1,87 +0,0 @@
-import loadStytch, { DiscoveredOrganizations } from "@/lib/stytch/loadStytch";
-import { getDiscoverySessionData } from "@/lib/stytch/sessionService";
-import { cookies } from "next/headers";
-import Link from "next/link";
-import { CardTitle, CardHeader, CardContent, CardDescription, Card } from "../ui/card";
-
-async function getProps() {
- const discoverySessionData = getDiscoverySessionData(
- cookies().get('session')?.value,
- cookies().get('intermediate_session')?.value,
- );
- if (discoverySessionData.error) {
- console.log("No session tokens found...");
- return { redirect: { statusCode: 307, destination: `/auth/login` } };
- }
-
- const { discovered_organizations } =
- await loadStytch().discovery.organizations.list({
- intermediate_session_token: discoverySessionData.intermediateSession,
- session_jwt: discoverySessionData.sessionJWT,
- });
-
- console.log(discovered_organizations);
-
- return {
- discovered_organizations
- };
-}
-
-type Props = {
- discovered_organizations: DiscoveredOrganizations;
-};
-
-const DiscoveredOrganizationsList = ({ discovered_organizations }: Props) => {
- const formatMembership = ({
- membership,
- organization,
- }: Pick) => {
- if (membership!.type === "pending_member") {
- return `Join ${organization!.organization_name}`;
- }
- if (membership!.type === "eligible_to_join_by_email_domain") {
- return `Join ${organization!.organization_name} via your ${membership!.details!.domain} email`;
- }
- if (membership!.type === "invited_member") {
- return `Accept Invite for ${organization!.organization_name}`;
- }
- return `Continue to ${organization!.organization_name}`;
- };
-
- return (
-
-
-
- Your Organizations
- {discovered_organizations.length === 0 && (
- No existing organizations.
- )}
-
-
- {discovered_organizations.map(({ organization, membership }) => (
-
-
-
-
- {formatMembership({ organization, membership })}
-
-
-
-
- ))}
-
-
-
- );
- };
-
-const DiscoveryServer = async () => {
- const {discovered_organizations} = await getProps();
- return (
-
- );
-};
-
-export default DiscoveryServer;
\ No newline at end of file
diff --git a/apps/client-ts/src/components/Auth/LoginDiscoveryForm.tsx b/apps/client-ts/src/components/Auth/LoginDiscoveryForm.tsx
deleted file mode 100644
index 7294babf2..000000000
--- a/apps/client-ts/src/components/Auth/LoginDiscoveryForm.tsx
+++ /dev/null
@@ -1,79 +0,0 @@
-'use client';
-
-import {FormEventHandler, useState} from "react";
-import {useRouter} from "next/navigation";
-import Link from "next/link";
-import {EmailLoginForm} from "./EmailLoginForm";
-import {discoveryStart} from "@/lib/stytch/api";
-import {OAuthButton, OAuthProviders} from "./OAuthButton";
-import { Input } from "@/components/ui/input"
-import { Button } from "../ui/button";
-import { Badge } from "../ui/badge";
-import { Card } from "../ui/card";
-
-const ContinueToTenantForm = ({ onBack }: { onBack: () => void }) => {
- const [slug, setSlug] = useState("");
- const router = useRouter();
-
- const onSubmit: FormEventHandler = async (e) => {
- e.preventDefault();
- router.push(`/auth/${slug}/login`);
- };
-
- return (
-
- Enter your organization
-
- Don't know your organization's Domain?
-
-
- Login to find your{" "}
-
- organizations
-
-
-
-
- );
-};
-
-type Props = { domain: string; };
-
-const LoginDiscoveryForm = ({domain}: Props) => {
- const [isDiscovery, setIsDiscovery] = useState(true);
-
- if (isDiscovery) {
- return (
- <>
-
-
- We'll email you a magic code for a password-free sign in.
-
- Or you can{" "}
- setIsDiscovery(false)}>
- sign in manually instead
-
- .
-
-
-
-
- >
- );
- } else {
- return setIsDiscovery(true)} />;
- }
-};
-
-export default LoginDiscoveryForm;
diff --git a/apps/client-ts/src/components/Auth/OAuthButton.tsx b/apps/client-ts/src/components/Auth/OAuthButton.tsx
deleted file mode 100644
index ce73950a6..000000000
--- a/apps/client-ts/src/components/Auth/OAuthButton.tsx
+++ /dev/null
@@ -1,40 +0,0 @@
-import React from 'react';
-import Link from "next/link";
-import GoogleIconSvg from "../../../public/icons/google";
-import {formatOAuthDiscoveryStartURL, formatOAuthStartURL} from "@/lib/stytch/loadStytch";
-import MicrosoftIconSvg from "../../../public/icons/microsoft";
-
-
-export enum OAuthProviders {
- Google = 'google',
- Microsoft = 'microsoft',
-}
-
-const providerInfo = {
- [OAuthProviders.Google]: {
- providerTypeTitle: 'Google',
- providerIcon: ,
- },
- [OAuthProviders.Microsoft]: {
- providerTypeTitle: 'Microsoft',
- providerIcon: ,
- }
-}
-
-type Props = {
- providerType: OAuthProviders;
- hostDomain: string;
- orgSlug?: string;
-};
-
-export const OAuthButton = ({ providerType, hostDomain, orgSlug }: Props) => {
- const isDiscovery = orgSlug == null;
- const oAuthStartURL = isDiscovery ? formatOAuthDiscoveryStartURL(hostDomain, providerType) : formatOAuthStartURL(hostDomain, providerType, orgSlug);
-
- return (
-
- {providerInfo[providerType].providerIcon}
- {`Continue with ${providerInfo[providerType].providerTypeTitle}`}
-
- );
-};
diff --git a/apps/client-ts/src/components/Auth/SignupForm.tsx b/apps/client-ts/src/components/Auth/SignupForm.tsx
deleted file mode 100644
index 886c4da33..000000000
--- a/apps/client-ts/src/components/Auth/SignupForm.tsx
+++ /dev/null
@@ -1,126 +0,0 @@
-'use client';
-
-import {
- ChangeEventHandler,
- FormEventHandler,
- useEffect,
- useState,
-} from "react";
-import { discoveryStart } from "@/lib/stytch/api";
-import {OAuthButton, OAuthProviders} from "./OAuthButton";
-import { Input } from "@/components/ui/input"
-
-const STATUS = {
- INIT: 0,
- SENT: 1,
- ERROR: 2,
-};
-
-const isValidEmail = (emailValue: string) => {
- // Overly simple email address regex
- const regex = /\S+@\S+\.\S+/;
- return regex.test(emailValue);
-};
-
-const isValidOrgName = (organizationName: string) => {
- return organizationName.length > 3;
-};
-
-type Props = { domain: string; };
-
-const SignupForm = ({ domain }: Props) => {
- const [emlSent, setEMLSent] = useState(STATUS.INIT);
- const [email, setEmail] = useState("");
- const [isDisabled, setIsDisabled] = useState(true);
-
- useEffect(() => {
- const isValid = isValidEmail(email);
- setIsDisabled(!isValid);
- }, [email]);
-
- const onEmailChange: ChangeEventHandler = (e) => {
- setEmail(e.target.value);
- if (isValidEmail(e.target.value)) {
- setIsDisabled(false);
- } else {
- setIsDisabled(true);
- }
- };
-
- const onSubmit: FormEventHandler = async (e) => {
- e.preventDefault();
- // Disable button right away to prevent sending emails twice
- if (isDisabled) {
- return;
- } else {
- setIsDisabled(true);
- }
-
- if (isValidEmail(email)) {
- const resp = await discoveryStart(email);
- if (resp.status === 200) {
- setEMLSent(STATUS.SENT);
- } else {
- setEMLSent(STATUS.ERROR);
- }
- }
- };
-
- const handleTryAgain = (e: any) => {
- e.preventDefault();
- e.stopPropagation();
- setEMLSent(STATUS.INIT);
- // setEmail('');
- // setOrganizationName('');
- };
-
- return (
-
- {emlSent === STATUS.INIT && (
- <>
-
Sign up
-
- or
-
-
- >
- )}
- {emlSent === STATUS.SENT && (
- <>
-
Check your email
-
{`An email was sent to ${email}`}
-
- Click here to try again.
-
- >
- )}
- {emlSent === STATUS.ERROR && (
-
- )}
-
- );
-};
-
-export default SignupForm;
diff --git a/apps/client-ts/src/components/Auth/TenantedLoginForm.tsx b/apps/client-ts/src/components/Auth/TenantedLoginForm.tsx
deleted file mode 100644
index 895aa0277..000000000
--- a/apps/client-ts/src/components/Auth/TenantedLoginForm.tsx
+++ /dev/null
@@ -1,44 +0,0 @@
-"use client"
-
-import { login } from "@/lib/stytch/api";
-import {
- formatSSOStartURL,
- Organization,
-} from "@/lib/stytch/loadStytch";
-import { EmailLoginForm } from "./EmailLoginForm";
-import {OAuthButton, OAuthProviders} from "./OAuthButton";
-import { Card } from "../ui/card";
-
-type Props = {
- org: Organization;
- domain: string;
-};
-const TenantedLoginForm = ({ org, domain }: Props) => {
- return (
-
- login(email, org.organization_id)}
- >
- {org.sso_default_connection_id && (
-
- )}
-
-
-
- {/* Login with Google*/}
- {/**/}
-
- );
-};
-
-
-export default TenantedLoginForm;
diff --git a/apps/client-ts/src/components/Auth/b2c/LoginWithStytchSDKUI.tsx b/apps/client-ts/src/components/Auth/b2c/LoginWithStytchSDKUI.tsx
deleted file mode 100644
index 15674fe51..000000000
--- a/apps/client-ts/src/components/Auth/b2c/LoginWithStytchSDKUI.tsx
+++ /dev/null
@@ -1,80 +0,0 @@
-'use client';
-
-import { StytchLogin } from '@stytch/nextjs';
-import { StytchLoginConfig, OAuthProviders, Products, StyleConfig, StytchEvent, StytchError } from '@stytch/vanilla-js';
-import { getDomainFromWindow } from '@/lib/stytch/urlUtils';
-import { Button } from '@/components/ui/button';
-import { Icons } from '@/components/shared/icons';
-
-const sdkStyle: StyleConfig = {
- fontFamily: '"Helvetica New", Helvetica, sans-serif',
- buttons: {
- primary: {
- backgroundColor: '#19303d',
- textColor: '#ffffff',
- },
- },
-};
-
-const sdkConfig: StytchLoginConfig = {
- products: [Products.emailMagicLinks],
- emailMagicLinksOptions: {
- loginRedirectURL: getDomainFromWindow() + '/authenticate',
- loginExpirationMinutes: 30,
- signupRedirectURL: getDomainFromWindow() + '/authenticate',
- signupExpirationMinutes: 30,
- createUserAsPending: false,
- }
-};
-
-const callbackConfig = {
- onEvent: (message: StytchEvent) => console.log(message),
- onError: (error: StytchError) => console.log(error),
-}
-
-const getOauthUrl = (provider: string) => {
- const isTest = process.env.NEXT_PUBLIC_STYTCH_PROJECT_ENV == 'test';
- const baseStytch = isTest ? "test" : "api"
- return `https://${baseStytch}.stytch.com/v1/public/oauth/${provider.toLowerCase()}/start?public_token=${process.env.NEXT_PUBLIC_STYTCH_PUBLIC_TOKEN}`;
-}
-
-const LoginWithStytchSDKUI = () => {
- return (
- <>
-
-
-
-
-
-
-
-
-
- Or continue with
-
-
-
-
-
- >
- )
-}
-
-export default LoginWithStytchSDKUI;
\ No newline at end of file
diff --git a/apps/client-ts/src/components/Connection/AddConnectionButton.tsx b/apps/client-ts/src/components/Connection/AddConnectionButton.tsx
index 1344dce95..341f451a3 100644
--- a/apps/client-ts/src/components/Connection/AddConnectionButton.tsx
+++ b/apps/client-ts/src/components/Connection/AddConnectionButton.tsx
@@ -169,6 +169,20 @@ const AddConnectionButton = ({
+ {idProject==="" ? (
+ <>
+
+
+
+ You have to create project in order to create Connection.
+
+
+
+ >
+ )
+ :
+ (
+ <>
Share this magic link with your customers
@@ -244,6 +258,8 @@ const AddConnectionButton = ({
+ >
+ )}
)
diff --git a/apps/client-ts/src/components/Connection/ConnectionTable.tsx b/apps/client-ts/src/components/Connection/ConnectionTable.tsx
index 32339635f..9cd911554 100644
--- a/apps/client-ts/src/components/Connection/ConnectionTable.tsx
+++ b/apps/client-ts/src/components/Connection/ConnectionTable.tsx
@@ -59,30 +59,30 @@ export default function ConnectionTable() {
return (
<>
-
-
-
- Linked
+
+
+
+ Linked
- {linkedConnections("valid")?.length}
+ {linkedConnections("valid")?.length}
-
-
- Incomplete Link
+
+
+ Incomplete Link
- {linkedConnections("1")?.length}
+ {linkedConnections("1")?.length}
-
-
- Relink Needed
+
+
+ Relink Needed
- {linkedConnections("2")?.length}
+ {linkedConnections("2")?.length}
diff --git a/apps/client-ts/src/components/Connection/columns.tsx b/apps/client-ts/src/components/Connection/columns.tsx
index 6c6283b8f..32e142b53 100644
--- a/apps/client-ts/src/components/Connection/columns.tsx
+++ b/apps/client-ts/src/components/Connection/columns.tsx
@@ -7,6 +7,10 @@ import { Checkbox } from "@/components/ui/checkbox"
import { Connection } from "./data/schema"
import { DataTableColumnHeader } from "./../shared/data-table-column-header"
+import React,{ useState } from "react"
+import { ClipboardIcon } from '@radix-ui/react-icons'
+import { toast } from "sonner"
+
function truncateMiddle(str: string, maxLength: number) {
if (str.length <= maxLength) {
@@ -20,10 +24,10 @@ function truncateMiddle(str: string, maxLength: number) {
function insertDots(originalString: string): string {
if(!originalString) return "";
- if (originalString.length <= 50) {
- return originalString;
- }
- return originalString.substring(0, 50 - 3) + '...';
+ // if (originalString.length <= 50) {
+ // return originalString;
+ // }
+ return originalString.substring(0, 7) + '...';
}
function formatISODate(ISOString: string): string {
@@ -44,6 +48,26 @@ function formatISODate(ISOString: string): string {
return formatter.format(date);
}
+const connectionTokenComponent = ({row}:{row:any}) => {
+ const handleCopy = async () => {
+ try {
+ await navigator.clipboard.writeText(row.getValue("connectionToken"));
+ toast("Connection Token copied to clipboard!!")
+ } catch (err) {
+ console.error('Failed to copy: ', err);
+ }
+ };
+
+ return (
+
+ {truncateMiddle(row.getValue("connectionToken"),6)}
+
+
+
+
+ )
+}
+
export const columns: ColumnDef[] = [
{
id: "select",
@@ -187,23 +211,17 @@ export const columns: ColumnDef[] = [
//const label = labels.find((label) => label.value === row.original.date)
return (
-
+
{formatISODate(row.getValue("date"))}
)
},
},
- /*{
+ {
accessorKey: "connectionToken",
header: ({ column }) => (
),
- cell: ({ row }) => {
-
-
- {insertDots(row.getValue("connectionToken"))}
-
-
- },
- }*/
+ cell: connectionTokenComponent
+ }
]
\ No newline at end of file
diff --git a/apps/client-ts/src/components/Nav/main-nav.tsx b/apps/client-ts/src/components/Nav/main-nav.tsx
index f51e83b42..913a06b28 100644
--- a/apps/client-ts/src/components/Nav/main-nav.tsx
+++ b/apps/client-ts/src/components/Nav/main-nav.tsx
@@ -11,15 +11,15 @@ export function MainNav({
onLinkClick: (name: string) => void;
className: string;
}) {
- const [selectedItem, setSelectedItem] = useState
("connections");
+ const [selectedItem, setSelectedItem] = useState("");
const pathname = usePathname();
useEffect(() => {
setSelectedItem(pathname.substring(1))
}, [pathname])
const navItemClassName = (itemName: string) =>
- `text-sm border-b font-medium w-full text-left px-4 py-2 dark:hover:bg-zinc-900 hover:bg-zinc-200 cursor-pointer ${
- selectedItem === itemName ? 'dark:bg-zinc-800 bg-zinc-200' : 'text-muted-foreground'
+ `group flex items-center rounded-md px-3 py-2 text-sm font-medium hover:bg-accent hover:text-accent-foreground cursor-pointer ${
+ selectedItem === itemName ? 'bg-accent' : 'transparent'
} transition-colors`;
function click(e: MouseEvent, name: string) {
@@ -29,7 +29,7 @@ export function MainNav({
return (