Skip to content

Commit

Permalink
Merge pull request #284 from mohamedsalem401/auth
Browse files Browse the repository at this point in the history
Add login/registration forms
  • Loading branch information
naelob authored Feb 24, 2024
2 parents 7b0426c + 2652b0a commit 8373ec5
Show file tree
Hide file tree
Showing 36 changed files with 797 additions and 54 deletions.
5 changes: 4 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ ENV=dev
DISTRIBUTION=selfhosted
OAUTH_REDIRECT_BASE=http://localhost:3000
SENTRY_DSN=

STYTCH_PROJECT_ID=
STYTCH_SECRET=
FRONT_END_URL=http://localhost
# ================================================
# Database
# ================================================
Expand Down Expand Up @@ -53,6 +55,7 @@ ZENDESK_TICKETING_CLIENT_SECRET=
# Must be set in the perspective of the end user browser
VITE_BACKEND_DOMAIN=http://localhost:3000
VITE_FRONTEND_DOMAIN=http://localhost:81
VITE_STYTCH_TOKEN=

# ================================================
# Minio (s3 file storage)
Expand Down
2 changes: 2 additions & 0 deletions apps/webapp/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,10 @@ ENV PATH="$PNPM_HOME:$PATH"

ARG VITE_BACKEND_DOMAIN
ARG VITE_FRONTEND_DOMAIN
ARG VITE_STYTCH_TOKEN
ENV VITE_BACKEND_DOMAIN="$VITE_BACKEND_DOMAIN"
ENV VITE_FRONTEND_DOMAIN="$VITE_FRONTEND_DOMAIN"
ENV VITE_STYTCH_TOKEN="$VITE_STYTCH_TOKEN"
RUN corepack enable

WORKDIR /app
Expand Down
2 changes: 2 additions & 0 deletions apps/webapp/Dockerfile.dev
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@ ENV PATH="$PNPM_HOME:$PATH"

ARG VITE_BACKEND_DOMAIN
ARG VITE_FRONTEND_DOMAIN
ARG VITE_STYTCH_TOKEN

ENV VITE_BACKEND_DOMAIN="$VITE_BACKEND_DOMAIN"
ENV VITE_FRONTEND_DOMAIN="$VITE_FRONTEND_DOMAIN"
ENV VITE_STYTCH_TOKEN="$VITE_STYTCH_TOKEN"

RUN corepack enable

Expand Down
2 changes: 2 additions & 0 deletions apps/webapp/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@
"@radix-ui/react-switch": "^1.0.3",
"@radix-ui/react-tabs": "^1.0.4",
"@radix-ui/react-tooltip": "^1.0.7",
"@stytch/react": "^15.0.0",
"@stytch/vanilla-js": "^4.5.3",
"@tanstack/react-query": "^5.12.2",
"@tanstack/react-table": "^8.10.7",
"antd": "^5.11.5",
Expand Down
21 changes: 14 additions & 7 deletions apps/webapp/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ import DashboardPage from './components/dashboard';
import useProfileStore from './state/profileStore';
import { usePostHog } from 'posthog-js/react';
import { useEffect } from 'react';
import RegisterPage from './routes/auth_.register';
import LoginPage from './routes/auth_.login';
import { Toaster } from 'sonner';

const queryClient = new QueryClient();

Expand All @@ -37,16 +40,20 @@ function App() {
<Router>
<QueryParamProvider adapter={ReactRouter6Adapter}>
<Routes>
<Route path='/auth/register' element={<RegisterPage />} />
<Route path='/auth/login' element={<LoginPage />} />

<Route path='/' element={<RootLayout />}>
<Route index element={<ConnectionsPage />} />
<Route path='/dashboard' element={<DashboardPage />} />
<Route path='/logs' element={<LogsPage />} />
<Route path='/tasks' element={<TaskPage />} />
<Route path='/configuration' element={<ConfigurationPage />} />
<Route path='/connections' element={<ConnectionsPage />} />
<Route path='/api-keys' element={<ApiKeysPage />} />
<Route index element={<ConnectionsPage />} />
<Route path='/dashboard' element={<DashboardPage />} />
<Route path='/logs' element={<LogsPage />} />
<Route path='/tasks' element={<TaskPage />} />
<Route path='/configuration' element={<ConfigurationPage />} />
<Route path='/connections' element={<ConnectionsPage />} />
<Route path='/api-keys' element={<ApiKeysPage />} />
</Route>
</Routes>
<Toaster />
</QueryParamProvider>
</Router>
</ThemeProvider>
Expand Down
Binary file added apps/webapp/src/assets/login.jpeg
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 apps/webapp/src/assets/register.jpeg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 9 additions & 0 deletions apps/webapp/src/components/auth/login/login-schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { z } from 'zod';
import { registerSchema } from '../register/register-schema';

export const loginSchema = registerSchema.pick({
email: true,
password: true,
});

export type LoginSchemaType = z.infer<typeof loginSchema>;
89 changes: 89 additions & 0 deletions apps/webapp/src/components/auth/login/login-user-form.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import { useForm } from 'react-hook-form';
import { Link, useNavigate } from 'react-router-dom';
import { zodResolver } from '@hookform/resolvers/zod';

import { Input } from '@/components/ui/input';
import { Button } from '@/components/ui/button';
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from '@/components/ui/form';
import { LoadingSpinner } from '@/components/connections/components/LoadingSpinner';

import useLoginMutation from '@/hooks/mutations/useLoginMutation';
import { type LoginSchemaType, loginSchema } from './login-schema';
import { toast } from 'sonner';

export const LoginUserForm = () => {
const navigate = useNavigate();
const { mutate: login, isPending: loginPending } = useLoginMutation();

const form = useForm<LoginSchemaType>({
resolver: zodResolver(loginSchema),
});

const onSubmit = async (data: LoginSchemaType) => {
login(data, {
onSuccess: () => {
navigate('/');
},
onError: () => {
toast.error("Failed to log in. Please verify your email and password and try again.");
},
});
};

return (
<div className='mx-auto w-full max-w-sm lg:w-96'>
<div className='text-center'>
<Link to='/'>
<img src='/logo.png' className='w-14 mx-auto' />
</Link>
<h2 className='mt-6 text-3xl font-extrabold'>Login</h2>
<p className='mt-2 text-sm'>
Don&apos;t have an account?{' '}
<Link to='/auth/register' className='font-medium text-primary hover:text-primary/80'>
Register
</Link>
</p>
</div>

<Form {...form}>
<form className='space-y-6 mt-8' onSubmit={form.handleSubmit(onSubmit)}>
<FormField
control={form.control}
name='email'
render={({ field }) => (
<FormItem>
<FormLabel htmlFor='email'>Email</FormLabel>
<FormControl>
<Input placeholder='[email protected]' autoComplete='email' {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>

<FormField
control={form.control}
name='password'
render={({ field }) => (
<FormItem>
<FormLabel htmlFor='password'>Password</FormLabel>
<FormControl>
<Input placeholder='********' type='password' autoComplete='current-password' {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>

<Link to='/auth/forgot-password' className='font-medium block text-sm text-primary hover:text-primary/80'>
Forgot your password?
</Link>

<Button type='submit' className='w-full' disabled={loginPending}>
{loginPending ? <LoadingSpinner className='' /> : 'Login'}
</Button>
</form>
</Form>
</div>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import { useForm } from "react-hook-form";
import { Link, useNavigate } from "react-router-dom";
import { zodResolver } from "@hookform/resolvers/zod";

import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button";
import { LoadingSpinner } from "@/components/connections/components/LoadingSpinner";

import useOrganisationMutation from "@/hooks/mutations/useOrganisationMutation";
import {
type OrganizationFormSchemaType,
organizationFormSchema,
} from "./create-organization-schema";

export const CreateOrganizationForm = () => {
const navigate = useNavigate();
const { mutate, isPending } = useOrganisationMutation();

const form = useForm<OrganizationFormSchemaType>({
resolver: zodResolver(organizationFormSchema),
});

const onSubmit = (data: OrganizationFormSchemaType) => {
mutate(
{ ...data, stripe_customer_id: "stripe-customer-76" },
{
onSuccess: () => navigate("/"),
}
);
};

return (
<div className="mx-auto w-full max-w-sm lg:w-96">
<div className="text-center">
<Link to="/">
<img src="/logo.png" className="w-14 mx-auto" />
</Link>
<h2 className="mt-6 text-3xl font-extrabold">Create Organization</h2>
</div>

<Form {...form}>
<form className="space-y-6 mt-8" onSubmit={form.handleSubmit(onSubmit)}>
<FormField
control={form.control}
name="name"
render={({ field }) => (
<FormItem>
<FormLabel htmlFor="name">Name</FormLabel>
<FormControl>
<Input
placeholder="organization"
autoComplete="given-name"
{...field}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<Button className="w-full" type="submit">
{isPending ? <LoadingSpinner className="" /> : "Create"}
</Button>
</form>
</Form>
<Link
to="/"
className="mt-4 text-sm font-medium text-primary hover:text-primary/80 text-center block mx-auto"
>
Skip For Now
</Link>
</div>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { z } from 'zod';

export const organizationFormSchema = z.object({
name: z.string().min(1, 'Name is required').max(100, 'Name must be less than 100 characters'),
stripe_customer_id: z.string().min(1, 'stripe customer id is required'),
});

export type OrganizationFormSchemaType = z.infer<typeof organizationFormSchema>;
12 changes: 12 additions & 0 deletions apps/webapp/src/components/auth/register/register-form.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { useState } from 'react';

import { RegisterUserForm } from './register-user-form';
import { CreateOrganizationForm } from './create-organization-form';

export const RegisterForm = () => {
const [registered, setIsRegistered] = useState(false);

if (!registered) return <RegisterUserForm onSuccess={() => setIsRegistered(true)} />;

return <CreateOrganizationForm />;
};
10 changes: 10 additions & 0 deletions apps/webapp/src/components/auth/register/register-schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { z } from 'zod';

export const registerSchema = z.object({
firstName: z.string().min(1, 'First name is required'),
lastName: z.string().min(1, 'Last name is required'),
email: z.string().min(1, 'Email is required').email('Email is invalid'),
password: z.string().min(8, 'Password must be at least 8 characters'),
});

export type RegisterSchemaType = z.infer<typeof registerSchema>;
Loading

0 comments on commit 8373ec5

Please sign in to comment.