Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Authentication, Forgot Password, Integration with FE #13

Merged
merged 1 commit into from
Feb 3, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions backend/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.env
432 changes: 300 additions & 132 deletions backend/cmd/server/main.go

Large diffs are not rendered by default.

34 changes: 34 additions & 0 deletions backend/config/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package config

import (
"fmt"
"io/ioutil"

"gopkg.in/yaml.v3"
)
type Config struct {
Server struct {
Port int `yaml:"port"`
} `yaml:"server"`

Cognito struct {
AppClientId string `yaml:"appClientId"`
AppClientSecret string `yaml:"appClientSecret"`
UserPoolId string `yaml:"userPoolId"`
Region string `yaml:"region"`
} `yaml:"cognito"`
}

func LoadConfig(path string) (*Config, error) {
data, err := ioutil.ReadFile(path)
if err != nil {
return nil, fmt.Errorf("failed to read config file: %w", err)
}

var cfg Config
if err := yaml.Unmarshal(data, &cfg); err != nil {
return nil, fmt.Errorf("failed to unmarshal yaml: %w", err)
}

return &cfg, nil
}
8 changes: 8 additions & 0 deletions backend/config/config.prod.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
server:
port: 8080

cognito:
appClientId:
appClientSecret:
userPoolId:
region:
1 change: 1 addition & 0 deletions backend/go.mod
Original file line number Diff line number Diff line change
@@ -27,6 +27,7 @@ require (
github.com/cloudwego/base64x v0.1.4 // indirect
github.com/cloudwego/iasm v0.2.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
github.com/gin-contrib/cors v1.7.2 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
3 changes: 3 additions & 0 deletions backend/go.sum
Original file line number Diff line number Diff line change
@@ -39,6 +39,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
github.com/gin-contrib/cors v1.7.2 h1:oLDHxdg8W/XDoN/8zamqk/Drgt4oVZDvaV0YmvVICQw=
github.com/gin-contrib/cors v1.7.2/go.mod h1:SUJVARKgQ40dmrzgXEVxj2m7Ig1v1qIboQkPDTQ9t2E=
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU=
@@ -112,6 +114,7 @@ google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFW
google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
Empty file removed backend/routes.go
Empty file.
15 changes: 0 additions & 15 deletions backend/todo.txt

This file was deleted.

26 changes: 26 additions & 0 deletions frontend/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*

node_modules
dist
dist-ssr
*.local

# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

.env
57,519 changes: 54,955 additions & 2,564 deletions frontend/package-lock.json

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion frontend/package.json
Original file line number Diff line number Diff line change
@@ -16,19 +16,21 @@
"@radix-ui/react-slot": "^1.1.0",
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.1",
"config": "^3.3.12",
"lucide-react": "^0.446.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-helmet": "^6.1.0",
"react-icons": "^5.3.0",
"react-router-dom": "^6.26.2",
"react-router-dom": "^6.28.0",
"tailwind-merge": "^2.5.2",
"tailwindcss-animate": "^1.0.7"
},
"devDependencies": {
"@aws-amplify/backend": "^1.4.0",
"@aws-amplify/backend-cli": "^1.2.9",
"@eslint/js": "^9.9.0",
"@svgr/rollup": "^8.1.0",
"@types/node": "^22.7.4",
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
59 changes: 35 additions & 24 deletions frontend/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,31 +1,42 @@
import { useContext, useState } from 'react'
import './App.css'
import AuthenticationPage from './Pages/Authentication'
import { ThemeProvider, ThemeContext } from './context/theme-provider'
import { Button } from './components/ui/button'
import './App.css';
import { BrowserRouter as Router, Routes, Route, Navigate } from 'react-router-dom';
import Authentication from './Pages/Authentication';
import Home from './Pages/Home';
import { ThemeProvider } from './context/theme-provider';

import { LuMoon } from "react-icons/lu";
import { LuSun } from "react-icons/lu";
// Dummy authentication check function (replace with real logic)
const isAuthenticated = (): boolean => {
// Example: Check if a token exists in localStorage
return true;
// return localStorage.getItem('authToken') ? true : false;
};


function Subscriber(){
const value = useContext(ThemeContext);
return(
<Button onClick={value!.toggleTheme} className='p-0 h-8 w-8 md:h-12 md:w-12 fixed right-4 bottom-4'>
{value?.theme ? <LuMoon className='text-xl'/> : <LuSun className="text-xl"/>}
</Button>
)
// Define props for ProtectedRoute
interface ProtectedRouteProps {
children: React.ReactNode; // React children
}
function App() {

// Protected Route Component
const ProtectedRoute: React.FC<ProtectedRouteProps> = ({ children }) => {
return isAuthenticated() ? <>{children}</> : <Navigate to="/auth" replace />;
};


function App() {
return (
<div>
<ThemeProvider>
<AuthenticationPage></AuthenticationPage>
{/* <Subscriber></Subscriber> */}
</ThemeProvider>
</div>
)
<ThemeProvider>
<Router>
<Routes>
<Route path="/auth" element={<Authentication />} />
<Route path="/" element={<Home />} />
</Routes>
</Router>
</ThemeProvider>
);
}

export default App
export default App;

{/* <ProtectedRoute>
<Home />
</ProtectedRoute> */}
6 changes: 6 additions & 0 deletions frontend/src/Pages/.prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"editor.defaultFormatter": "esbenp.prettier-vscode",
"[javascript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
}
}
179 changes: 158 additions & 21 deletions frontend/src/Pages/Authentication.tsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,162 @@
import React from 'react'
import { LoginForm } from "./Authentication/form"
import { Separator } from "@/components/ui/separator"
import React, { useState } from 'react';
import { Button } from '@/components/ui/button';
import { LoginForm, SignUpForm, OTPVerificationForm, ForgotPasswordForm, ResetPasswordForm } from './Authentication/forms.tsx';


const LeftSection = () => (

<div className="hidden md:flex w-full h-full flex-col justify-between bg-muted p-10 text-white">
<div className="flex items-center text-lg font-medium">
<Link to="/" className="flex items-center">
{/* Logo and App Name */}
<svg /* SVG attributes */>
{/* SVG Content */}
</svg>
Arguehub
</Link>
</div>
<div>
<blockquote className="space-y-2">
<p className="text-lg">
"We cannot solve our problems with the same thinking we used when we created them."
</p>
<footer className="text-sm">Albert Einstein</footer>
</blockquote>
</div>
</div>
);



interface RightSectionProps {
authMode: 'login' | 'signup' | 'otpVerification' | 'forgotPassword' | 'resetPassword';
toggleAuthMode: () => void;
startOtpVerification: (email: string) => void;
handleOtpVerified: () => void;
startForgotPassword: () => void;
startResetPassword: (email: string) => void; // New prop
handlePasswordReset: () => void; // New prop
emailForOTP: string;
emailForPasswordReset: string; // New prop
infoMessage: string; // New prop
}

const RightSection: React.FC<RightSectionProps> = ({
authMode,
toggleAuthMode,
startOtpVerification,
handleOtpVerified,
startForgotPassword,
startResetPassword,
handlePasswordReset,
emailForOTP,
emailForPasswordReset,
infoMessage,
}) => (
<div className="flex items-center justify-center w-full h-full relative">
{authMode !== 'otpVerification' && authMode !== 'resetPassword' && (
<Button
className="absolute right-4 top-4 md:right-8 md:top-8"
onClick={toggleAuthMode}
variant="outline"
>
{authMode === 'signup' ? 'Sign In' : 'Sign Up'}
</Button>
)}
<div className="flex flex-col items-center justify-center h-full w-3/5 text-center">
{authMode === 'login' && (
<>
<h3 className="text-2xl font-medium my-4">Sign in to your account</h3>
<LoginForm startForgotPassword={startForgotPassword} infoMessage={infoMessage} />
</>
)}
{authMode === 'signup' && (
<>
<h3 className="text-2xl font-medium my-4">Create an account</h3>
<SignUpForm startOtpVerification={startOtpVerification} />
</>
)}
{authMode === 'otpVerification' && (
<OTPVerificationForm email={emailForOTP} handleOtpVerified={handleOtpVerified} />
)}
{authMode === 'forgotPassword' && (
<ForgotPasswordForm startResetPassword={startResetPassword} />
)}
{authMode === 'resetPassword' && (
<ResetPasswordForm
email={emailForPasswordReset}
handlePasswordReset={handlePasswordReset}
/>
)}
</div>
</div>
);

import { Link } from 'react-router-dom';

const Authentication = () => {
// Extend authMode to include 'resetPassword'
const [authMode, setAuthMode] = useState<
'login' | 'signup' | 'otpVerification' | 'forgotPassword' | 'resetPassword'
>('login');

const [emailForOTP, setEmailForOTP] = useState('');
const [emailForPasswordReset, setEmailForPasswordReset] = useState(''); // New state for reset password
const [infoMessage, setInfoMessage] = useState('');

// Toggle between 'login' and 'signup'
const toggleAuthMode = () => {
setAuthMode((prevMode) => (prevMode === 'login' ? 'signup' : 'login'));
};

// Start OTP verification process
const startOtpVerification = (email: string) => {
setEmailForOTP(email);
setAuthMode('otpVerification');
};

// Handle successful OTP verification
const handleOtpVerified = () => {
setAuthMode('login');
};

// Start forgot password process
const startForgotPassword = () => {
setAuthMode('forgotPassword');
};

// Start reset password process
const startResetPassword = (email: string) => {
setEmailForPasswordReset(email);
setAuthMode('resetPassword');
};

// Handle successful password reset
const handlePasswordReset = () => {
setInfoMessage('Your password was successfully reset. You can now log in.');
setAuthMode('login');
};

return (
<div className='flex w-screen h-screen box-border'>
{/* overflow-hidden */}
<div className='hidden md:flex w-full h-full justify-center items-center'>keshav</div>
<div className='flex items-center justify-center w-full h-full'>
<div className='flex flex-col items-center justify-center h-full w-3/4 text-center'>
<LoginForm></LoginForm>
<div className='flex items-center w-1/2'>
<div className="bg-border h-px w-full" />
<p className='text-xs mx-2 w-full'>OR CONTINUE WITH</p>
<div className="bg-border h-px w-full" />
</div>
{/* todo */}
<p>By clicking continue, you agree to our <a href='https://www.optmyzr.com' target='_blank'>Terms of Service</a> and <a>Privacy Policy</a>.</p>
</div>
</div>
<div className="flex w-screen h-screen">
{/* LeftSection component remains the same */}
<LeftSection />

{/* Pass new props to RightSection */}
<RightSection
authMode={authMode}
toggleAuthMode={toggleAuthMode}
startOtpVerification={startOtpVerification}
handleOtpVerified={handleOtpVerified}
startForgotPassword={startForgotPassword}
startResetPassword={startResetPassword} // New prop
handlePasswordReset={handlePasswordReset} // New prop
emailForOTP={emailForOTP}
emailForPasswordReset={emailForPasswordReset} // New prop
infoMessage={infoMessage} // New prop
/>
</div>
)
}
);
};

export default Authentication
export default Authentication;
29 changes: 0 additions & 29 deletions frontend/src/Pages/Authentication/form.tsx

This file was deleted.

353 changes: 353 additions & 0 deletions frontend/src/Pages/Authentication/forms.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,353 @@
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { useState } from 'react';

let baseURL = import.meta.env.VITE_BASE_URL


interface LoginFormProps {
startForgotPassword: () => void;
infoMessage?: string;
}

export const LoginForm: React.FC<LoginFormProps> = ({ startForgotPassword, infoMessage }) => {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [error, setError] = useState('');

const baseURL = import.meta.env.VITE_BASE_URL;

const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setError('');

try {
const response = await fetch(`${baseURL}/login`, {
method: 'POST',
credentials: 'include',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email, password }),
});

if (!response.ok) {
const data = await response.json();
setError(data.message || 'Failed to sign in. Please try again.');
return;
}

// Handle successful login
} catch {
setError('An unexpected error occurred. Please try again later.');
}
};

return (
<form className="w-full" onSubmit={handleSubmit}>
{infoMessage && <p className="text-sm text-green-500 mb-2">{infoMessage}</p>}
<Input
type="email"
placeholder="name@example.com"
value={email}
onChange={(e) => setEmail(e.target.value)}
className="mb-2"
/>
<Input
type="password"
placeholder="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
className="mb-1"
/>
{error && <p className="text-sm text-red-500 mb-2">{error}</p>}
<p className="text-sm text-muted mb-4">
Forgot your password?{' '}
<span className="underline cursor-pointer" onClick={startForgotPassword}>
Reset Password
</span>
</p>
<Button type="submit" className="w-full">
Sign In With Email
</Button>
</form>
);
};

interface SignUpFormProps {
startOtpVerification: (email: string) => void;
}

export const SignUpForm: React.FC<SignUpFormProps> = ({ startOtpVerification }) => {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [confirmPassword, setConfirmPassword] = useState('');
const [error, setError] = useState('');

const baseURL = import.meta.env.VITE_BASE_URL;

const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setError('');

if (password !== confirmPassword) {
setError('Passwords do not match.');
return;
}

try {
const response = await fetch(`${baseURL}/signup`, {
method: 'POST',
credentials: 'include',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email, password }),
});

if (!response.ok) {
const data = await response.json();
setError(data.message || 'Failed to sign up. Please try again.');
return;
}

// Start OTP verification phase
startOtpVerification(email);
} catch {
setError('An unexpected error occurred. Please try again later.');
}
};

return (
<form className="w-full" onSubmit={handleSubmit}>
<Input
type="email"
placeholder="name@example.com"
value={email}
onChange={(e) => setEmail(e.target.value)}
className="mb-2"
/>
<Input
type="password"
placeholder="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
className="mb-2"
/>
<Input
type="password"
placeholder="confirm password"
value={confirmPassword}
onChange={(e) => setConfirmPassword(e.target.value)}
className="mb-4"
/>
{error && <p className="text-sm text-red-500 mb-2">{error}</p>}
<Button type="submit" className="w-full">
Sign Up With Email
</Button>
</form>
);
};

interface OTPVerificationFormProps {
email: string;
handleOtpVerified: () => void;
}

export const OTPVerificationForm: React.FC<OTPVerificationFormProps> = ({ email, handleOtpVerified }) => {
const [otp, setOtp] = useState('');
const [error, setError] = useState('');
const [loading, setLoading] = useState(false);

const baseURL = import.meta.env.VITE_BASE_URL;

const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setError('');
setLoading(true);

try {
const response = await fetch(`${baseURL}/verifyEmail`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email, confirmationCode: otp }),
});

if (!response.ok) {
const data = await response.json();
setError(data.message || 'Failed to verify OTP. Please try again.');
setLoading(false);
return;
}

// OTP verified successfully
handleOtpVerified();
} catch {
setError('An unexpected error occurred. Please try again later.');
setLoading(false);
}
};

return (
<div className="w-full flex flex-col items-center">
<h3 className="text-2xl font-medium my-4">Verify Your Email</h3>
<p className="mb-4">Enter the OTP sent to your email to complete the sign-up process.</p>
<form onSubmit={handleSubmit} className="w-full">
<Input
type="text"
value={otp}
onChange={(e) => setOtp(e.target.value)}
placeholder="Enter OTP"
className="w-full mb-4"
/>
{error && <p className="text-sm text-red-500 mb-2">{error}</p>}
<Button type="submit" className="w-full" disabled={loading}>
{loading ? 'Verifying...' : 'Verify OTP'}
</Button>
</form>
</div>
);
};



interface ForgotPasswordFormProps {
startResetPassword: (email: string) => void; // Accept the new prop
}

export const ForgotPasswordForm: React.FC<ForgotPasswordFormProps> = ({
startResetPassword,
}) => {
const [email, setEmail] = useState('');
const [error, setError] = useState('');

const baseURL = import.meta.env.VITE_BASE_URL;

const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setError('');

try {
const response = await fetch(`${baseURL}/forgotPassword`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email }),
});

if (!response.ok) {
setError('Failed to send reset password code. Please try again.');
return;
}

// Move to the ResetPasswordForm
startResetPassword(email);
} catch {
setError('An unexpected error occurred. Please try again later.');
}
};

return (
<div className="w-full flex flex-col items-center">
<h3 className="text-2xl font-medium my-4">Reset Password</h3>
<p className="mb-4">Enter your email to receive a password reset code.</p>
<form onSubmit={handleSubmit} className="w-full">
<Input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="name@example.com"
className="w-full mb-4"
/>
{error && <p className="text-sm text-red-500 mb-2">{error}</p>}
<Button type="submit" className="w-full">
Send Reset Code
</Button>
</form>
</div>
);
};


interface ResetPasswordFormProps {
email: string;
handlePasswordReset: () => void;
}

export const ResetPasswordForm: React.FC<ResetPasswordFormProps> = ({
email,
handlePasswordReset,
}) => {
const [code, setCode] = useState('');
const [newPassword, setNewPassword] = useState('');
const [confirmNewPassword, setConfirmNewPassword] = useState('');
const [error, setError] = useState('');
const [loading, setLoading] = useState(false);

const baseURL = import.meta.env.VITE_BASE_URL;

const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setError('');
setLoading(true);

if (newPassword !== confirmNewPassword) {
setError('Passwords do not match.');
setLoading(false);
return;
}

try {
const response = await fetch(`${baseURL}/confirmForgotPassword`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
email,
code,
newPassword,
}),
});

if (!response.ok) {
const data = await response.json();
setError(data.message || 'Failed to reset password. Please try again.');
setLoading(false);
return;
}

// Password reset successfully
handlePasswordReset();
} catch {
setError('An unexpected error occurred. Please try again later.');
setLoading(false);
}
};

return (
<div className="w-full flex flex-col items-center">
<h3 className="text-2xl font-medium my-4">Reset Your Password</h3>
<form onSubmit={handleSubmit} className="w-full">
<Input
type="text"
value={code}
onChange={(e) => setCode(e.target.value)}
placeholder="Enter Code"
className="w-full mb-2"
/>
<Input
type="password"
value={newPassword}
onChange={(e) => setNewPassword(e.target.value)}
placeholder="New Password"
className="w-full mb-2"
/>
<Input
type="password"
value={confirmNewPassword}
onChange={(e) => setConfirmNewPassword(e.target.value)}
placeholder="Confirm New Password"
className="w-full mb-4"
/>
{error && <p className="text-sm text-red-500 mb-2">{error}</p>}
<Button type="submit" className="w-full" disabled={loading}>
{loading ? 'Resetting Password...' : 'Reset Password'}
</Button>
</form>
</div>
);
};
84 changes: 84 additions & 0 deletions frontend/src/Pages/Home.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import DebateCover from "../assets/DebateCover4.svg";
import { Button } from "@/components/ui/button";
import { useNavigate } from "react-router-dom";
import { RiRobot2Fill } from "react-icons/ri";
import { FaHandshakeSimpleSlash } from "react-icons/fa6";
import { useState } from "react";

const Home: React.FC = () => {
const navigate = useNavigate();
const [isAuthenticated, setIsAuthenticated] = useState<boolean>(false);
var signupHandler = () => {
navigate('/auth', { state: { isSignUp: true } });
}
var loginHandler = () => {
navigate('/auth', { state: { isSignUp: false } });
}

const handlePlayDebateClick = () => {
if (isAuthenticated) {
navigate('/play');
} else {
navigate('/auth', { state: { isSignUp: false } });
}
};

const handlePlayBotClick = () => {
if (isAuthenticated) {
navigate('/play'); // Navigate to play page if authenticated
} else {
navigate('/auth', { state: { isSignUp: false } }); // Navigate to login page if not authenticated
}
};

return (
<div className="flex flex-col min-h-screen">
<nav className="flex items-center justify-between px-4 py-4 md:px-12">
<h1 className="text-xl md:text-3xl font-bold">Argue-Hub</h1>
<div className="flex">
<Button className="mr-2" onClick={loginHandler}>Login</Button>
<Button variant="outline" onClick={signupHandler}>Sign Up</Button>
</div>
</nav>

<div className="flex items-center justify-center">
<div className="flex flex-wrap items-center justify-center w-full px-2 md:px-16">
<div className="w-full md:w-2/3 p-4 md:p-16">
<img src={DebateCover} alt="Debate Cover" className="w-full object-cover" />
</div>
<div className="flex w-full md:w-1/3 flex-col items-center justify-center space-y-4 p-4">
<h3 className="text-xl md:text-4xl font-bold text-center">
Play Debate Online on the <span className="text-primary">#1</span> Site!
</h3>
<div className="flex flex-col w-full">
<Button className="my-2 h-auto rounded text-xl flex items-center justify-start" onClick={handlePlayDebateClick}>
<FaHandshakeSimpleSlash className="text-4xl" />
<div className="flex flex-col items-start ml-4">
<span className="font-bold">Play Online</span>
<span className="text-sm text-primary-foreground font-thin">
Play with someone at your level
</span>
</div>
</Button>
<Button
className="my-2 h-auto rounded text-xl flex items-center justify-start"
variant="outline"
onClick={handlePlayBotClick}
>
<RiRobot2Fill className="text-4xl" />
<div className="flex flex-col items-start ml-4">
<span className="font-bold">Practice with Bot</span>
<span className="text-sm text-muted-foreground font-thin">
Improve your skills with AI guidance
</span>
</div>
</Button>
</div>
</div>
</div>
</div>
</div>
);
};

export default Home;
437 changes: 437 additions & 0 deletions frontend/src/assets/DebateCover4.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
75 changes: 39 additions & 36 deletions frontend/src/index.css
Original file line number Diff line number Diff line change
@@ -2,60 +2,63 @@
@tailwind components;
@tailwind utilities;
@layer base {

:root {
--background: 0 0% 100%;
--foreground: 222.2 84% 4.9%;
--foreground: 20 14.3% 4.1%;
--card: 0 0% 100%;
--card-foreground: 222.2 84% 4.9%;
--card-foreground: 20 14.3% 4.1%;
--popover: 0 0% 100%;
--popover-foreground: 222.2 84% 4.9%;
--primary: 222.2 47.4% 11.2%;
--primary-foreground: 210 40% 98%;
--secondary: 210 40% 96.1%;
--secondary-foreground: 222.2 47.4% 11.2%;
--muted: 210 40% 96.1%;
--muted-foreground: 215.4 16.3% 46.9%;
--accent: 210 40% 96.1%;
--accent-foreground: 222.2 47.4% 11.2%;
--popover-foreground: 20 14.3% 4.1%;
--primary: 24.6 95% 53.1%;
--primary-foreground: 60 9.1% 97.8%;
--secondary: 60 4.8% 95.9%;
--secondary-foreground: 24 9.8% 10%;
--muted: 60 4.8% 95.9%;
--muted-foreground: 25 5.3% 44.7%;
--accent: 60 4.8% 95.9%;
--accent-foreground: 24 9.8% 10%;
--destructive: 0 84.2% 60.2%;
--destructive-foreground: 210 40% 98%;
--border: 214.3 31.8% 91.4%;
--input: 214.3 31.8% 91.4%;
--ring: 222.2 84% 4.9%;
--destructive-foreground: 60 9.1% 97.8%;
--border: 20 5.9% 90%;
--input: 20 5.9% 90%;
--ring: 24.6 95% 53.1%;
--radius: 0.5rem;
--chart-1: 12 76% 61%;
--chart-2: 173 58% 39%;
--chart-3: 197 37% 24%;
--chart-4: 43 74% 66%;
--chart-5: 27 87% 67%;
--radius: 0.5rem
}

.dark {
--background: 222.2 84% 4.9%;
--foreground: 210 40% 98%;
--card: 222.2 84% 4.9%;
--card-foreground: 210 40% 98%;
--popover: 222.2 84% 4.9%;
--popover-foreground: 210 40% 98%;
--primary: 210 40% 98%;
--primary-foreground: 222.2 47.4% 11.2%;
--secondary: 217.2 32.6% 17.5%;
--secondary-foreground: 210 40% 98%;
--muted: 217.2 32.6% 17.5%;
--muted-foreground: 215 20.2% 65.1%;
--accent: 217.2 32.6% 17.5%;
--accent-foreground: 210 40% 98%;
--destructive: 0 62.8% 30.6%;
--destructive-foreground: 210 40% 98%;
--border: 217.2 32.6% 17.5%;
--input: 217.2 32.6% 17.5%;
--ring: 212.7 26.8% 83.9%;
--background: 20 14.3% 4.1%;
--foreground: 60 9.1% 97.8%;
--card: 20 14.3% 4.1%;
--card-foreground: 60 9.1% 97.8%;
--popover: 20 14.3% 4.1%;
--popover-foreground: 60 9.1% 97.8%;
--primary: 20.5 90.2% 48.2%;
--primary-foreground: 60 9.1% 97.8%;
--secondary: 12 6.5% 15.1%;
--secondary-foreground: 60 9.1% 97.8%;
--muted: 12 6.5% 15.1%;
--muted-foreground: 24 5.4% 63.9%;
--accent: 12 6.5% 15.1%;
--accent-foreground: 60 9.1% 97.8%;
--destructive: 0 72.2% 50.6%;
--destructive-foreground: 60 9.1% 97.8%;
--border: 12 6.5% 15.1%;
--input: 12 6.5% 15.1%;
--ring: 20.5 90.2% 48.2%;
--chart-1: 220 70% 50%;
--chart-2: 160 60% 45%;
--chart-3: 30 80% 55%;
--chart-4: 280 65% 60%;
--chart-5: 340 75% 55%
--chart-5: 340 75% 55%;
}
}

@layer base {
* {
@apply border-border;
3 changes: 2 additions & 1 deletion frontend/vite.config.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import path from "path"
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react-swc'
import svgr from "@svgr/rollup"; // Import the plugin

// https://vitejs.dev/config/
export default defineConfig({
plugins: [react()],
plugins: [react(), svgr(), ],

//shadcn
resolve: {