Skip to content

Commit

Permalink
refactor: improve code organization and fix voice input bug
Browse files Browse the repository at this point in the history
  • Loading branch information
Sma1lboy committed Oct 29, 2024
1 parent f2575a3 commit 42c7a88
Show file tree
Hide file tree
Showing 6 changed files with 159 additions and 28 deletions.
85 changes: 68 additions & 17 deletions frontend/src/app/hooks/useAuth.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,21 @@
import { useState, useEffect } from 'react';
import { useQuery, useMutation } from '@apollo/client';
import { useRouter } from 'next/navigation';
import { toast } from 'sonner';
import { LoginResponse, LoginUserInput } from '@/graphql/type';
import { CHECK_TOKEN_QUERY, LOGIN_MUTATION } from '@/graphql/auth';
import {
LoginResponse,
LoginUserInput,
RegisterUserInput,
} from '@/graphql/type';
import {
CHECK_TOKEN_QUERY,
LOGIN_MUTATION,
REGISTER_MUTATION,
} from '@/graphql/auth';
import { LocalStore } from '@/lib/storage';

export const useAuth = () => {
const router = useRouter();
const [isAuthenticated, setIsAuthenticated] = useState(false);

const [login, { loading: loginLoading }] = useMutation<{
login: LoginResponse;
}>(LOGIN_MUTATION);
Expand All @@ -20,6 +27,12 @@ export const useAuth = () => {
}
);

const [register, { loading: registerLoading }] = useMutation<{
registerUser: {
username: string;
};
}>(REGISTER_MUTATION);

useEffect(() => {
validateToken();
}, []);
Expand All @@ -28,30 +41,35 @@ export const useAuth = () => {
const token = localStorage.getItem(LocalStore.accessToken);
if (!token) {
setIsAuthenticated(false);
router.push('/login');
return;
return { success: false };
}

try {
const { data } = await checkToken({
input: { token },
});

if (data?.checkToken) {
setIsAuthenticated(true);
toast.success('Authentication validated');
router.push('/');
return { success: true };
} else {
localStorage.removeItem(LocalStore.accessToken);
setIsAuthenticated(false);
toast.error('Session expired, please login again');
router.push('/login');
return { success: false, error: 'Session expired' };
}
} catch (error) {
localStorage.removeItem(LocalStore.accessToken);
setIsAuthenticated(false);
toast.error('Authentication error, please login again');
router.push('/login');
return {
success: false,
error: error instanceof Error ? error.message : 'Authentication error',
};
}
};

const handleLogin = async (credentials: LoginUserInput) => {
try {
const { data } = await login({
Expand All @@ -64,28 +82,61 @@ export const useAuth = () => {
localStorage.setItem(LocalStore.accessToken, data.login.accessToken);
setIsAuthenticated(true);
toast.success('Login successful');
router.push('/');
return true;
return { success: true };
}

return false;
return { success: false };
} catch (error) {
toast.error(error instanceof Error ? error.message : 'Login failed');
return false;
return {
success: false,
error: error instanceof Error ? error.message : 'Login failed',
};
}
};

const handleLogout = () => {
localStorage.removeItem('accessToken');
localStorage.removeItem(LocalStore.accessToken);
setIsAuthenticated(false);
router.push('/login');
toast.success('Logged out successfully');
return { success: true };
};

const handleRegister = async (credentials: {
username: string;
email: string;
password: string;
}) => {
try {
const { data } = await register({
variables: {
input: credentials,
},
});

if (data?.registerUser?.username) {
toast.success('Registration successful');
return await handleLogin({
username: credentials.username,
password: credentials.password,
});
}
return { success: false };
} catch (error) {
toast.error(
error instanceof Error ? error.message : 'Registration failed'
);
return {
success: false,
error: error instanceof Error ? error.message : 'Registration failed',
};
}
};

return {
isAuthenticated,
isLoading: loginLoading,
isLoading: loginLoading || registerLoading,
login: handleLogin,
register: handleRegister,
logout: handleLogout,
validateToken,
};
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import { RootProvider } from './provider';
const inter = Inter({ subsets: ['latin'] });

export const metadata: Metadata = {
title: 'Ollama UI',
description: 'Ollama chatbot web interface',
title: 'Codefox',
description: 'The best dev project generator',
};

export const viewport = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@ import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { useAuth } from '@/app/hooks/useAuth';
import { useRouter } from 'next/navigation';

interface LoginFormData {
username: string;
password: string;
}

const LoginPage = () => {
const { login, isLoading, isAuthenticated } = useAuth();
const { login, isLoading, isAuthenticated, validateToken } = useAuth();
const [formData, setFormData] = useState<LoginFormData>({
username: '',
password: '',
Expand All @@ -26,20 +27,34 @@ const LoginPage = () => {
}));
};

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

try {
await login({
const res = await login({
username: formData.username,
password: formData.password,
});
if (res.success) {
router.push('/');
}
} catch (err) {
setError(err instanceof Error ? err.message : 'Login failed');
}
};

useEffect(() => {
const checkAuth = async () => {
const result = await validateToken();
if (result.success) {
router.push('/');
}
};
checkAuth();
}, []);

return (
<div className="flex items-center justify-center min-h-screen bg-light-background dark:bg-dark-background">
<div className="w-full max-w-md px-8">
Expand Down
1 change: 1 addition & 0 deletions frontend/src/app/provider.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
'use client';
import { LocalStore } from '@/lib/storage';
import client from '@/utils/client';
import { ApolloProvider } from '@apollo/client';
import { ThemeProvider } from 'next-themes';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,33 +1,77 @@
'use client';

import { useState } from 'react';
import { useEffect, useState } from 'react';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Check, PartyPopper } from 'lucide-react';
import { useAuth } from '@/app/hooks/useAuth';
import { useRouter } from 'next/navigation';

type Step = 'welcome' | 'form' | 'success' | 'congrats';

export default function Register() {
const router = useRouter();
const { register, isLoading, isAuthenticated, validateToken } = useAuth();
const [step, setStep] = useState<Step>('welcome');
const [formData, setFormData] = useState({
name: '',
email: '',
password: '',
});

useEffect(() => {
if (isAuthenticated) {
router.push('/');
} else {
validateToken();
}
}, [isAuthenticated, router, validateToken]);

const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const { name, value } = e.target;
setFormData((prev) => ({ ...prev, [name]: value }));
};

const handleCreateAccount = () => {
const handleCreateAccount = async () => {
if (formData.name && formData.email && formData.password) {
console.log('Registering:', formData);
setStep('success');
setTimeout(() => setStep('congrats'), 1000);

try {
const success = await register({
username: formData.name,
email: formData.email,
password: formData.password,
});

if (success) {
setStep('congrats');
setTimeout(() => {
router.push('/');
}, 2000);
} else {
setStep('form');
}
} catch (error) {
setStep('form');
}
}
};

const handleEnterChat = () => {
console.log('enter');
router.push('/');
};

useEffect(() => {
const checkAuth = async () => {
const result = await validateToken();
if (result.success) {
router.push('/');
}
};
checkAuth();
}, []);

return (
<div className="flex items-center justify-center min-h-screen bg-light-background dark:bg-dark-background overflow-hidden">
<div className="w-full max-w-md p-8 relative">
Expand All @@ -47,12 +91,14 @@ export default function Register() {
</p>
<Button
onClick={() => setStep('form')}
disabled={isLoading}
className="w-full h-12 bg-primary-500 hover:bg-primary-600 dark:bg-primary-600 dark:hover:bg-primary-700 text-white rounded-lg text-lg"
>
Start
</Button>
</div>

{/* Form Section */}
<div
className={`transition-all duration-500 ease-in-out
${
Expand All @@ -77,6 +123,7 @@ export default function Register() {
value={formData.name}
onChange={handleInputChange}
required
disabled={isLoading}
className="w-full h-12 px-4 rounded-lg border-light-border dark:border-dark-border
bg-light-surface dark:bg-dark-surface
text-light-text-primary dark:text-dark-text-primary
Expand All @@ -95,6 +142,7 @@ export default function Register() {
value={formData.email}
onChange={handleInputChange}
required
disabled={isLoading}
className="w-full h-12 px-4 rounded-lg border-light-border dark:border-dark-border
bg-light-surface dark:bg-dark-surface
text-light-text-primary dark:text-dark-text-primary
Expand All @@ -113,6 +161,7 @@ export default function Register() {
value={formData.password}
onChange={handleInputChange}
required
disabled={isLoading}
className="w-full h-12 px-4 rounded-lg border-light-border dark:border-dark-border
bg-light-surface dark:bg-dark-surface
text-light-text-primary dark:text-dark-text-primary
Expand All @@ -128,10 +177,16 @@ export default function Register() {

<Button
onClick={handleCreateAccount}
disabled={
isLoading ||
!formData.name ||
!formData.email ||
!formData.password
}
className="w-full h-12 bg-primary-500 hover:bg-primary-600 dark:bg-primary-600 dark:hover:bg-primary-700
text-white rounded-lg text-lg"
>
Create Account
{isLoading ? 'Creating Account...' : 'Create Account'}
</Button>
</div>
</div>
Expand Down Expand Up @@ -174,10 +229,12 @@ export default function Register() {
</div>
<div className="pt-6">
<Button
onClick={handleEnterChat}
disabled={isLoading}
className="w-full h-12 bg-primary-500 hover:bg-primary-600 dark:bg-primary-600 dark:hover:bg-primary-700
text-white rounded-lg text-lg transform transition-transform hover:scale-105"
text-white rounded-lg text-lg transform transition-transform hover:scale-105"
>
Enter Chat
{isLoading ? 'Loading...' : 'Enter Chat'}
</Button>
</div>
</div>
Expand Down
7 changes: 7 additions & 0 deletions frontend/src/graphql/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,10 @@ export const CHECK_TOKEN_QUERY = gql`
checkToken(input: $input)
}
`;
export const REGISTER_MUTATION = gql`
mutation RegisterUser($input: RegisterUserInput!) {
registerUser(input: $input) {
username
}
}
`;

0 comments on commit 42c7a88

Please sign in to comment.