feat(hackathon): update landing page and auth page UI (#60)

* feat: update landing page

* feat: Add favicon and SEO (JSON-LD schema, meta tags)

- Add favicon image
- Add JSON-LD schema for Hackathon event
- Add react-helmet-async for managing meta tags

* chore: remove react-helmet-async

* chore: update landing page

* chore: update UI for signup and login

- add back button for better UX

---------

Co-authored-by: Maulana Sodiqin <sodiqincahyana1@gmail.com>
This commit is contained in:
Hafid Nur
2025-11-25 19:59:08 +07:00
committed by GitHub
co-authored by Maulana Sodiqin
parent 8947b4d05e
commit ab744b411d
7 changed files with 364 additions and 271 deletions
-3
View File
@@ -4,7 +4,6 @@
<meta charset="utf-8" /> <meta charset="utf-8" />
<title>IMPHNEN x Kolosal.ai Hackathon 2025</title> <title>IMPHNEN x Kolosal.ai Hackathon 2025</title>
<base href="/" /> <base href="/" />
<meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="viewport" content="width=device-width, initial-scale=1" />
<meta <meta
name="description" name="description"
@@ -20,7 +19,6 @@
href="/images/imphnen-logo-simple.svg" href="/images/imphnen-logo-simple.svg"
/> />
<link rel="stylesheet" href="/src/index.css" /> <link rel="stylesheet" href="/src/index.css" />
<!-- Open Graph -->
<meta property="og:type" content="website" /> <meta property="og:type" content="website" />
<meta property="og:url" content="https://hackathon.imphnen.dev/" /> <meta property="og:url" content="https://hackathon.imphnen.dev/" />
<meta property="og:title" content="IMPHNEN x Kolosal.ai Hackathon 2025" /> <meta property="og:title" content="IMPHNEN x Kolosal.ai Hackathon 2025" />
@@ -28,7 +26,6 @@
property="og:description" property="og:description"
content="Total hadiah Rp14.500.000! Daftar sekarang dan wujudkan inovasi AI-mu untuk membantu usaha lokal." content="Total hadiah Rp14.500.000! Daftar sekarang dan wujudkan inovasi AI-mu untuk membantu usaha lokal."
/> />
<meta property="twitter:card" content="summary_large_image" /> <meta property="twitter:card" content="summary_large_image" />
<meta property="twitter:url" content="https://hackathon.imphnen.dev/" /> <meta property="twitter:url" content="https://hackathon.imphnen.dev/" />
<meta <meta
+46 -34
View File
@@ -13,62 +13,72 @@ const CallbackPage: FC = (): ReactElement => {
useEffect(() => { useEffect(() => {
const handleCallback = async () => { const handleCallback = async () => {
if (hasRunRef.current) { if (hasRunRef.current) {
console.log('[Callback] Already processed, skipping...'); // console.log('[Callback] Already processed, skipping...');
return; return;
} }
hasRunRef.current = true; hasRunRef.current = true;
try { try {
console.log('[Callback] Processing OAuth callback...'); // console.log('[Callback] Processing OAuth callback...');
console.log('[Callback] Current URL:', globalThis.location.href); // console.log('[Callback] Current URL:', globalThis.location.href);
// Supabase client is configured with detectSessionInUrl: true // Supabase client is configured with detectSessionInUrl: true
// This means Supabase automatically detects and processes OAuth tokens from the URL hash // This means Supabase automatically detects and processes OAuth tokens from the URL hash
// We just need to wait a moment for it to complete, then check for the session // We just need to wait a moment for it to complete, then check for the session
console.log('[Callback] Waiting for Supabase to process OAuth callback...'); // console.log('[Callback] Waiting for Supabase to process OAuth callback...');
await new Promise(resolve => setTimeout(resolve, 1000)); await new Promise((resolve) => setTimeout(resolve, 1000));
// Get the session that Supabase automatically created from the URL hash // Get the session that Supabase automatically created from the URL hash
const { data: { session: sessionData }, error: sessionError } = await supabase.auth.getSession(); const {
data: { session: sessionData },
error: sessionError,
} = await supabase.auth.getSession();
if (sessionError) { if (sessionError) {
console.error('[Callback] Session error:', sessionError); // console.error('[Callback] Session error:', sessionError);
throw new Error(sessionError.message || 'Failed to get session'); throw new Error(sessionError.message || 'Failed to get session');
} }
if (!sessionData || !sessionData.user) { if (!sessionData || !sessionData.user) {
throw new Error('No session found after OAuth callback. Please try logging in again.'); throw new Error(
'No session found after OAuth callback. Please try logging in again.'
);
} }
console.log('[Callback] Supabase session established:', { // console.log('[Callback] Supabase session established:', {
userId: sessionData.user.id, // userId: sessionData.user.id,
email: sessionData.user.email, // email: sessionData.user.email,
}); // });
// Create/update user in the users table (for foreign key constraints) // Create/update user in the users table (for foreign key constraints)
console.log('[Callback] Creating/updating user record...'); // console.log('[Callback] Creating/updating user record...');
const { data: userData, error: upsertError } = await supabase const { data: userData, error: upsertError } = await supabase
.from('users') .from('users')
.upsert({ .upsert(
id: sessionData.user.id, {
email: sessionData.user.email || '', id: sessionData.user.id,
fullname: sessionData.user.user_metadata?.full_name || email: sessionData.user.email || '',
sessionData.user.user_metadata?.name || fullname:
sessionData.user.email?.split('@')[0] || '', sessionData.user.user_metadata?.full_name ||
avatar: sessionData.user.user_metadata?.avatar_url || '', sessionData.user.user_metadata?.name ||
is_active: true, sessionData.user.email?.split('@')[0] ||
updated_at: new Date().toISOString(), '',
}, { avatar: sessionData.user.user_metadata?.avatar_url || '',
onConflict: 'id', is_active: true,
}) updated_at: new Date().toISOString(),
},
{
onConflict: 'id',
}
)
.select() .select()
.single(); .single();
if (upsertError) { if (upsertError) {
console.warn('[Callback] Failed to create user record:', upsertError); // console.warn('[Callback] Failed to create user record:', upsertError);
// Don't throw - continue with login even if user record creation fails // Don't throw - continue with login even if user record creation fails
} else { } else {
console.log('[Callback] User record created/updated successfully'); // console.log('[Callback] User record created/updated successfully');
} }
// Store user-friendly data in Zustand for UI purposes // Store user-friendly data in Zustand for UI purposes
@@ -77,9 +87,11 @@ const CallbackPage: FC = (): ReactElement => {
const userRecord = userData || { const userRecord = userData || {
id: sessionData.user.id, id: sessionData.user.id,
email: sessionData.user.email || '', email: sessionData.user.email || '',
fullname: sessionData.user.user_metadata?.full_name || fullname:
sessionData.user.user_metadata?.name || sessionData.user.user_metadata?.full_name ||
sessionData.user.email?.split('@')[0] || '', sessionData.user.user_metadata?.name ||
sessionData.user.email?.split('@')[0] ||
'',
avatar: sessionData.user.user_metadata?.avatar_url || '', avatar: sessionData.user.user_metadata?.avatar_url || '',
phone_number: '', phone_number: '',
birthdate: '', birthdate: '',
@@ -114,21 +126,21 @@ const CallbackPage: FC = (): ReactElement => {
}, },
}); });
console.log('[Callback] Session stored successfully'); // console.log('[Callback] Session stored successfully');
toast.success('Login successful!'); toast.success('Login successful!');
setIsProcessing(false); setIsProcessing(false);
// Check if user has completed onboarding (has location) // Check if user has completed onboarding (has location)
// Use globalThis.location.replace for hard redirect to prevent history issues // Use globalThis.location.replace for hard redirect to prevent history issues
if (userRecord.location) { if (userRecord.location) {
console.log('[Callback] User has completed onboarding, redirecting to dashboard...'); // console.log('[Callback] User has completed onboarding, redirecting to dashboard...');
globalThis.location.replace('/dashboard'); globalThis.location.replace('/dashboard');
} else { } else {
console.log('[Callback] User needs onboarding, redirecting...'); // console.log('[Callback] User needs onboarding, redirecting...');
globalThis.location.replace('/onboarding/user'); globalThis.location.replace('/onboarding/user');
} }
} catch (err) { } catch (err) {
console.error('[Callback] Error:', err); // console.error('[Callback] Error:', err);
setError((err as Error).message); setError((err as Error).message);
setIsProcessing(false); setIsProcessing(false);
toast.error('An error occurred during login'); toast.error('An error occurred during login');
@@ -1,12 +1,14 @@
import { useState } from 'react'; import { useState } from 'react';
import { supabase } from '@imphnen-frontend-service/service'; import { supabase } from '@imphnen-frontend-service/service';
import { Link } from 'react-router'; import { Link, useNavigate } from 'react-router';
import { toast } from 'sonner'; import { toast } from 'sonner';
import { Icon } from '@iconify/react';
export default function ForgotPasswordPage() { export default function ForgotPasswordPage() {
const [email, setEmail] = useState(''); const [email, setEmail] = useState('');
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
const [emailSent, setEmailSent] = useState(false); const [emailSent, setEmailSent] = useState(false);
const navigate = useNavigate();
const handleSubmit = async (e: React.FormEvent) => { const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault(); e.preventDefault();
@@ -30,7 +32,7 @@ export default function ForgotPasswordPage() {
setEmailSent(true); setEmailSent(true);
toast.success('Password reset email sent! Check your inbox.'); toast.success('Password reset email sent! Check your inbox.');
} catch (err) { } catch (err) {
console.error('Failed to send reset email:', err); // console.error('Failed to send reset email:', err);
toast.error((err as Error).message || 'Failed to send reset email'); toast.error((err as Error).message || 'Failed to send reset email');
} finally { } finally {
setIsLoading(false); setIsLoading(false);
@@ -55,7 +57,8 @@ export default function ForgotPasswordPage() {
<div className="space-y-4"> <div className="space-y-4">
<p className="text-sm text-gray-600"> <p className="text-sm text-gray-600">
Click the link in the email to reset your password. The link will expire in 1 hour. Click the link in the email to reset your password. The link will
expire in 1 hour.
</p> </p>
<Link to="/auth/login"> <Link to="/auth/login">
@@ -90,7 +93,10 @@ export default function ForgotPasswordPage() {
<form onSubmit={handleSubmit} className="space-y-4"> <form onSubmit={handleSubmit} className="space-y-4">
<div> <div>
<label htmlFor="email" className="block text-sm font-medium text-gray-700 mb-1"> <label
htmlFor="email"
className="block text-sm font-medium text-gray-700 mb-1"
>
Email Address Email Address
</label> </label>
<input <input
@@ -114,9 +120,13 @@ export default function ForgotPasswordPage() {
</button> </button>
</form> </form>
<div className="mt-6 text-center"> <div className="mt-8">
<Link to="/auth/login" className="text-primary-600 hover:text-primary-700 font-semibold"> <Link
Back to Login to="/auth/login"
className="text-primary-500 hover:text-primary-600 flex items-center"
>
<Icon icon="ic:baseline-chevron-left" width="24" height="24" />
<span> Back to Login</span>
</Link> </Link>
</div> </div>
</div> </div>
+44 -17
View File
@@ -1,11 +1,17 @@
import { useState } from 'react'; import { useState } from 'react';
import { useGitHubAuth, useEmailAuth, supabase, useAuthStore } from '@imphnen-frontend-service/service'; import {
useGitHubAuth,
useEmailAuth,
supabase,
useAuthStore,
} from '@imphnen-frontend-service/service';
import { GithubOutlined } from '@ant-design/icons'; import { GithubOutlined } from '@ant-design/icons';
import { useNavigate, Link } from 'react-router'; import { useNavigate, Link } from 'react-router';
import { toast } from 'sonner'; import { toast } from 'sonner';
import { Icon } from '@iconify/react';
export default function LoginPage() { export default function LoginPage() {
console.log('[LoginPage] Rendering...'); // console.log('[LoginPage] Rendering...');
const navigate = useNavigate(); const navigate = useNavigate();
const { setSession } = useAuthStore(); const { setSession } = useAuthStore();
@@ -28,10 +34,10 @@ export default function LoginPage() {
try { try {
setIsEmailLoading(true); setIsEmailLoading(true);
console.log('[Login] Attempting email login...'); // console.log('[Login] Attempting email login...');
const result = await signInWithEmail(email, password); const result = await signInWithEmail(email, password);
console.log('[Login] Email login successful:', result); // console.log('[Login] Email login successful:', result);
// Get user data from database // Get user data from database
const { data: userData } = await supabase const { data: userData } = await supabase
@@ -49,7 +55,8 @@ export default function LoginPage() {
user: { user: {
id: result.user.id, id: result.user.id,
email: result.user.email || '', email: result.user.email || '',
fullname: userData?.fullname || result.user.user_metadata?.full_name || '', fullname:
userData?.fullname || result.user.user_metadata?.full_name || '',
phone_number: userData?.phone_number || '', phone_number: userData?.phone_number || '',
avatar: userData?.avatar || '', avatar: userData?.avatar || '',
birthdate: userData?.birthdate || '', birthdate: userData?.birthdate || '',
@@ -86,22 +93,22 @@ export default function LoginPage() {
const handleGithubLogin = async () => { const handleGithubLogin = async () => {
try { try {
setIsGithubLoading(true); setIsGithubLoading(true);
console.log('[Login] Initiating GitHub OAuth...'); // console.log('[Login] Initiating GitHub OAuth...');
const result = await signInWithGitHub(); const result = await signInWithGitHub();
console.log('[Login] OAuth result:', result); // console.log('[Login] OAuth result:', result);
// Check if we got a redirect URL // Check if we got a redirect URL
if (result?.url) { if (result?.url) {
console.log('[Login] Redirecting to GitHub OAuth:', result.url); // console.log('[Login] Redirecting to GitHub OAuth:', result.url);
// Manually redirect immediately // Manually redirect immediately
globalThis.location.href = result.url; globalThis.location.href = result.url;
} else { } else {
console.error('[Login] No OAuth URL returned'); // console.error('[Login] No OAuth URL returned');
setIsGithubLoading(false); setIsGithubLoading(false);
} }
} catch (error) { } catch (error) {
console.error('[Login] GitHub login failed:', error); // console.error('[Login] GitHub login failed');
setIsGithubLoading(false); setIsGithubLoading(false);
} }
}; };
@@ -109,11 +116,19 @@ export default function LoginPage() {
return ( return (
<div className="flex justify-center items-center min-h-screen bg-gray-50 p-4"> <div className="flex justify-center items-center min-h-screen bg-gray-50 p-4">
<div className="bg-white w-full max-w-md p-8 rounded-2xl shadow-lg border border-gray-200"> <div className="bg-white w-full max-w-md p-8 rounded-2xl shadow-lg border border-gray-200">
<button
onClick={() => navigate('/')}
className="cursor-pointer text-primary-500 hover:text-primary-600 text-base font-sans flex items-center mb-6"
>
<Icon icon="ic:baseline-chevron-left" width="24" height="24" />
Back to Homepage
</button>
<div className="text-center mb-8"> <div className="text-center mb-8">
<h2 className="text-3xl font-bold text-gray-900 mb-2"> <h2 className="text-3xl font-bold text-gray-900 mb-2">
Welcome Back Welcome Back
</h2> </h2>
<p className="text-gray-600"> <p className="text-gray-600 font-sans">
Sign in to join or create your hackathon team Sign in to join or create your hackathon team
</p> </p>
</div> </div>
@@ -126,7 +141,10 @@ export default function LoginPage() {
<form onSubmit={handleEmailLogin} className="space-y-4"> <form onSubmit={handleEmailLogin} className="space-y-4">
<div> <div>
<label htmlFor="email" className="block text-sm font-medium text-gray-700 mb-1"> <label
htmlFor="email"
className="block text-sm font-medium text-gray-700 mb-1"
>
Email Email
</label> </label>
<input <input
@@ -143,10 +161,16 @@ export default function LoginPage() {
<div> <div>
<div className="flex items-center justify-between mb-1"> <div className="flex items-center justify-between mb-1">
<label htmlFor="password" className="block text-sm font-medium text-gray-700"> <label
htmlFor="password"
className="block text-sm font-medium text-gray-700"
>
Password Password
</label> </label>
<Link to="/auth/forgot-password" className="text-sm text-primary-600 hover:text-primary-700"> <Link
to="/auth/forgot-password"
className="text-sm text-primary-600 hover:text-primary-700"
>
Forgot password? Forgot password?
</Link> </Link>
</div> </div>
@@ -165,7 +189,7 @@ export default function LoginPage() {
<button <button
type="submit" type="submit"
disabled={isEmailLoading} disabled={isEmailLoading}
className="w-full py-3 bg-primary-600 text-white rounded-lg font-semibold hover:bg-primary-700 focus:outline-none focus:ring-2 focus:ring-primary-500 focus:ring-offset-2 disabled:bg-gray-400 disabled:cursor-not-allowed transition-colors" className="w-full py-3 bg-primary-600 text-white rounded-lg font-semibold hover:bg-primary-700 focus:outline-none focus:ring-2 focus:ring-primary-500 focus:ring-offset-2 disabled:bg-gray-400 disabled:cursor-not-allowed transition-colors cursor-pointer"
> >
{isEmailLoading ? 'Signing in...' : 'Sign in with Email'} {isEmailLoading ? 'Signing in...' : 'Sign in with Email'}
</button> </button>
@@ -181,7 +205,7 @@ export default function LoginPage() {
onClick={handleGithubLogin} onClick={handleGithubLogin}
disabled={isGithubLoading} disabled={isGithubLoading}
type="button" type="button"
className="w-full py-3 flex items-center justify-center gap-2 bg-gray-100 border border-gray-300 rounded-lg font-semibold hover:bg-gray-200 focus:outline-none focus:ring-2 focus:ring-gray-400 focus:ring-offset-2 disabled:bg-gray-100 disabled:cursor-not-allowed transition-colors" className="w-full py-3 flex items-center justify-center gap-2 bg-gray-100 border border-gray-300 rounded-lg font-semibold hover:bg-gray-200 focus:outline-none focus:ring-2 focus:ring-gray-400 focus:ring-offset-2 disabled:bg-gray-100 disabled:cursor-not-allowed transition-colors cursor-pointer"
> >
<GithubOutlined className="text-xl" /> <GithubOutlined className="text-xl" />
<span> <span>
@@ -192,7 +216,10 @@ export default function LoginPage() {
<div className="mt-6 text-center"> <div className="mt-6 text-center">
<p className="text-gray-600 text-sm"> <p className="text-gray-600 text-sm">
Don't have an account?{' '} Don't have an account?{' '}
<a href="/auth/signup" className="text-primary-600 hover:text-primary-700 font-semibold"> <a
href="/auth/signup"
className="text-primary-600 hover:text-primary-700 font-semibold"
>
Sign up Sign up
</a> </a>
</p> </p>
@@ -39,7 +39,7 @@ export default function ResetPasswordPage() {
setIsLoading(true); setIsLoading(true);
const { error } = await supabase.auth.updateUser({ const { error } = await supabase.auth.updateUser({
password: password password: password,
}); });
if (error) { if (error) {
@@ -52,7 +52,7 @@ export default function ResetPasswordPage() {
await supabase.auth.signOut(); await supabase.auth.signOut();
navigate('/auth/login'); navigate('/auth/login');
} catch (err) { } catch (err) {
console.error('Failed to reset password:', err); // console.error('Failed to reset password:', err);
toast.error((err as Error).message || 'Failed to reset password'); toast.error((err as Error).message || 'Failed to reset password');
} finally { } finally {
setIsLoading(false); setIsLoading(false);
@@ -77,14 +77,15 @@ export default function ResetPasswordPage() {
<h2 className="text-3xl font-bold text-gray-900 mb-2"> <h2 className="text-3xl font-bold text-gray-900 mb-2">
Set New Password Set New Password
</h2> </h2>
<p className="text-gray-600"> <p className="text-gray-600">Enter your new password below</p>
Enter your new password below
</p>
</div> </div>
<form onSubmit={handleSubmit} className="space-y-4"> <form onSubmit={handleSubmit} className="space-y-4">
<div> <div>
<label htmlFor="password" className="block text-sm font-medium text-gray-700 mb-1"> <label
htmlFor="password"
className="block text-sm font-medium text-gray-700 mb-1"
>
New Password New Password
</label> </label>
<input <input
@@ -101,7 +102,10 @@ export default function ResetPasswordPage() {
</div> </div>
<div> <div>
<label htmlFor="confirmPassword" className="block text-sm font-medium text-gray-700 mb-1"> <label
htmlFor="confirmPassword"
className="block text-sm font-medium text-gray-700 mb-1"
>
Confirm New Password Confirm New Password
</label> </label>
<input <input
+56 -26
View File
@@ -1,8 +1,14 @@
import { useState } from 'react'; import { useState } from 'react';
import { useGitHubAuth, useEmailAuth, supabase, useAuthStore } from '@imphnen-frontend-service/service'; import {
useGitHubAuth,
useEmailAuth,
supabase,
useAuthStore,
} from '@imphnen-frontend-service/service';
import { GithubOutlined } from '@ant-design/icons'; import { GithubOutlined } from '@ant-design/icons';
import { useNavigate } from 'react-router'; import { useNavigate } from 'react-router';
import { toast } from 'sonner'; import { toast } from 'sonner';
import { Icon } from '@iconify/react';
export default function SignupPage() { export default function SignupPage() {
const navigate = useNavigate(); const navigate = useNavigate();
@@ -38,30 +44,31 @@ export default function SignupPage() {
try { try {
setIsEmailLoading(true); setIsEmailLoading(true);
console.log('[Signup] Attempting email signup...'); // console.log('[Signup] Attempting email signup...');
const result = await signUpWithEmail(email, password, fullname); const result = await signUpWithEmail(email, password, fullname);
console.log('[Signup] Email signup successful:', result); // console.log('[Signup] Email signup successful:', result);
if (!result.user) { if (!result.user) {
throw new Error('Signup failed - no user returned'); throw new Error('Signup failed - no user returned');
} }
// Create user record in database // Create user record in database
const { error: upsertError } = await supabase const { error: upsertError } = await supabase.from('users').upsert(
.from('users') {
.upsert({
id: result.user.id, id: result.user.id,
email: result.user.email || '', email: result.user.email || '',
fullname: fullname, fullname: fullname,
is_active: true, is_active: true,
updated_at: new Date().toISOString(), updated_at: new Date().toISOString(),
}, { },
{
onConflict: 'id', onConflict: 'id',
}); }
);
if (upsertError) { if (upsertError) {
console.warn('[Signup] Failed to create user record:', upsertError); console.warn('[Signup] Failed to create user record');
} }
// If session is available (email confirmation disabled), store it // If session is available (email confirmation disabled), store it
@@ -94,13 +101,15 @@ export default function SignupPage() {
navigate('/onboarding/user'); navigate('/onboarding/user');
} else { } else {
// Email confirmation is enabled // Email confirmation is enabled
toast.success('Account created! Please check your email to verify your account.'); toast.success(
'Account created! Please check your email to verify your account.'
);
setTimeout(() => { setTimeout(() => {
navigate('/auth/login'); navigate('/auth/login');
}, 2000); }, 2000);
} }
} catch (err) { } catch (err) {
console.error('[Signup] Email signup failed:', err); // console.error('[Signup] Email signup failed:', err);
setError((err as Error).message || 'Signup failed'); setError((err as Error).message || 'Signup failed');
setIsEmailLoading(false); setIsEmailLoading(false);
} }
@@ -109,20 +118,20 @@ export default function SignupPage() {
const handleGithubLogin = async () => { const handleGithubLogin = async () => {
try { try {
setIsGithubLoading(true); setIsGithubLoading(true);
console.log('[Signup] Initiating GitHub OAuth...'); // console.log('[Signup] Initiating GitHub OAuth...');
const result = await signInWithGitHub(); const result = await signInWithGitHub();
console.log('[Signup] OAuth result:', result); // console.log('[Signup] OAuth result:', result);
if (result?.url) { if (result?.url) {
console.log('[Signup] Redirecting to GitHub OAuth:', result.url); // console.log('[Signup] Redirecting to GitHub OAuth:', result.url);
globalThis.location.href = result.url; globalThis.location.href = result.url;
} else { } else {
console.error('[Signup] No OAuth URL returned'); // console.error('[Signup] No OAuth URL returned');
setIsGithubLoading(false); setIsGithubLoading(false);
} }
} catch (error) { } catch (error) {
console.error('[Signup] GitHub login failed:', error); // console.error('[Signup] GitHub login failed:', error);
setIsGithubLoading(false); setIsGithubLoading(false);
} }
}; };
@@ -130,13 +139,19 @@ export default function SignupPage() {
return ( return (
<div className="flex justify-center items-center min-h-screen bg-gray-50 p-4"> <div className="flex justify-center items-center min-h-screen bg-gray-50 p-4">
<div className="bg-white w-full max-w-md p-8 rounded-2xl shadow-lg border border-gray-200"> <div className="bg-white w-full max-w-md p-8 rounded-2xl shadow-lg border border-gray-200">
<button
onClick={() => navigate('/')}
className="cursor-pointer text-primary-500 hover:text-primary-600 text-base font-sans flex items-center mb-6"
>
<Icon icon="ic:baseline-chevron-left" width="24" height="24" />
Back to Homepage
</button>
<div className="text-center mb-8"> <div className="text-center mb-8">
<h2 className="text-3xl font-bold text-gray-900 mb-2"> <h2 className="text-3xl font-bold text-gray-900 mb-2">
Create Account Create Account
</h2> </h2>
<p className="text-gray-600"> <p className="text-gray-600">Join the hackathon community</p>
Join the hackathon community
</p>
</div> </div>
{error && ( {error && (
@@ -147,7 +162,10 @@ export default function SignupPage() {
<form onSubmit={handleEmailSignup} className="space-y-4"> <form onSubmit={handleEmailSignup} className="space-y-4">
<div> <div>
<label htmlFor="fullname" className="block text-sm font-medium text-gray-700 mb-1"> <label
htmlFor="fullname"
className="block text-sm font-medium text-gray-700 mb-1"
>
Full Name Full Name
</label> </label>
<input <input
@@ -163,7 +181,10 @@ export default function SignupPage() {
</div> </div>
<div> <div>
<label htmlFor="email" className="block text-sm font-medium text-gray-700 mb-1"> <label
htmlFor="email"
className="block text-sm font-medium text-gray-700 mb-1"
>
Email Email
</label> </label>
<input <input
@@ -179,7 +200,10 @@ export default function SignupPage() {
</div> </div>
<div> <div>
<label htmlFor="password" className="block text-sm font-medium text-gray-700 mb-1"> <label
htmlFor="password"
className="block text-sm font-medium text-gray-700 mb-1"
>
Password Password
</label> </label>
<input <input
@@ -195,7 +219,10 @@ export default function SignupPage() {
</div> </div>
<div> <div>
<label htmlFor="confirmPassword" className="block text-sm font-medium text-gray-700 mb-1"> <label
htmlFor="confirmPassword"
className="block text-sm font-medium text-gray-700 mb-1"
>
Confirm Password Confirm Password
</label> </label>
<input <input
@@ -213,7 +240,7 @@ export default function SignupPage() {
<button <button
type="submit" type="submit"
disabled={isEmailLoading} disabled={isEmailLoading}
className="w-full py-3 bg-primary-600 text-white rounded-lg font-semibold hover:bg-primary-700 focus:outline-none focus:ring-2 focus:ring-primary-500 focus:ring-offset-2 disabled:bg-gray-400 disabled:cursor-not-allowed transition-colors" className="w-full py-3 bg-primary-600 text-white rounded-lg font-semibold hover:bg-primary-700 focus:outline-none focus:ring-2 focus:ring-primary-500 focus:ring-offset-2 disabled:bg-gray-400 disabled:cursor-not-allowed transition-colors cursor-pointer"
> >
{isEmailLoading ? 'Creating account...' : 'Create Account'} {isEmailLoading ? 'Creating account...' : 'Create Account'}
</button> </button>
@@ -229,7 +256,7 @@ export default function SignupPage() {
onClick={handleGithubLogin} onClick={handleGithubLogin}
disabled={isGithubLoading} disabled={isGithubLoading}
type="button" type="button"
className="w-full py-3 flex items-center justify-center gap-2 bg-gray-100 border border-gray-300 rounded-lg font-semibold hover:bg-gray-200 focus:outline-none focus:ring-2 focus:ring-gray-400 focus:ring-offset-2 disabled:bg-gray-100 disabled:cursor-not-allowed transition-colors" className="w-full py-3 flex items-center justify-center gap-2 bg-gray-100 border border-gray-300 rounded-lg font-semibold hover:bg-gray-200 focus:outline-none focus:ring-2 focus:ring-gray-400 focus:ring-offset-2 disabled:bg-gray-100 disabled:cursor-not-allowed transition-colors cursor-pointer"
> >
<GithubOutlined className="text-xl" /> <GithubOutlined className="text-xl" />
<span> <span>
@@ -240,7 +267,10 @@ export default function SignupPage() {
<div className="mt-6 text-center"> <div className="mt-6 text-center">
<p className="text-gray-600 text-sm"> <p className="text-gray-600 text-sm">
Already have an account?{' '} Already have an account?{' '}
<a href="/auth/login" className="text-primary-600 hover:text-primary-700 font-semibold"> <a
href="/auth/login"
className="text-primary-600 hover:text-primary-700 font-semibold"
>
Sign in Sign in
</a> </a>
</p> </p>
+190 -177
View File
@@ -64,71 +64,74 @@ export default function HomePage() {
return ( return (
<div className="min-h-screen bg-white"> <div className="min-h-screen bg-white">
{/* Navigation */} {/* Navigation */}
<nav className="flex items-center justify-between px-4 md:px-8 py-4 border-b border-gray-200 bg-white sticky top-0 z-50 max-w-7xl mx-auto"> <div id="#top" className="hidden"></div>
<div className="flex items-center gap-2"> <nav className="border-b border-gray-200 bg-white sticky top-0 z-50">
<span className="text-lg md:text-xl font-bold">IMPHNEN</span> <div className="flex items-center justify-between max-w-7xl mx-auto px-4 md:px-8 py-4">
<span className="text-lg md:text-xl font-bold text-primary-500"> <div className="flex items-center gap-2">
Hackathon <span className="text-lg md:text-xl font-bold">IMPHNEN</span>
</span> <a
href="#top"
className="text-lg md:text-xl font-bold text-primary-500 hover:cursor-pointer"
>
Hackathon
</a>
</div>
{/* Desktop Menu */}
<div className="hidden md:flex text-label1 items-center gap-4 lg:gap-8">
<a
href="#timeline"
className="text-gray-600 hover:text-gray-900 transition-colors"
>
Timeline
</a>
<a
href="#hadiah"
className="text-gray-600 hover:text-gray-900 transition-colors"
>
Hadiah
</a>
<a
href="#faq"
className="text-gray-600 hover:text-gray-900 transition-colors"
>
FAQ
</a>
<Button
onClick={() => navigate('/auth/login')}
size="sm"
variant="bordered"
className="rounded-lg text-base"
>
Masuk
</Button>
<Button
onClick={() => navigate('/auth/signup')}
size="sm"
className="rounded-lg text-base"
>
Daftar Sekarang
</Button>
</div>
{/* Mobile Menu Button */}
<button
onClick={() => setMobileMenuOpen(!mobileMenuOpen)}
className="md:hidden p-2 cursor-pointer"
>
<svg
className="w-6 h-6"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M4 6h16M4 12h16M4 18h16"
/>
</svg>
</button>
</div> </div>
{/* Desktop Menu */}
<div className="hidden md:flex text-label1 items-center gap-4 lg:gap-8">
<a
href="#timeline"
className="text-gray-600 hover:text-gray-900 transition-colors"
>
Timeline
</a>
<a
href="#hadiah"
className="text-gray-600 hover:text-gray-900 transition-colors"
>
Hadiah
</a>
<a
href="#faq"
className="text-gray-600 hover:text-gray-900 transition-colors"
>
FAQ
</a>
<Button
onClick={() => navigate('/auth/login')}
size="sm"
variant="bordered"
className="rounded-lg text-base"
>
Masuk
</Button>
<Button
onClick={() => navigate('/auth/signup')}
size="sm"
className="rounded-lg text-base"
>
Daftar Sekarang
</Button>
</div>
{/* Mobile Menu Button */}
<button
onClick={() => setMobileMenuOpen(!mobileMenuOpen)}
className="md:hidden p-2 cursor-pointer"
>
<svg
className="w-6 h-6"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M4 6h16M4 12h16M4 18h16"
/>
</svg>
</button>
</nav> </nav>
{/* Mobile Menu */} {/* Mobile Menu */}
@@ -164,90 +167,96 @@ export default function HomePage() {
)} )}
{/* Hero Section */} {/* Hero Section */}
<section className="flex flex-col items-center justify-center px-4 md:px-8 py-20 md:py-20 bg-linear-to-b from-white to-gray-50"> <section className="relative w-full overflow-hidden py-20">
{/* Logos */} <div className="absolute inset-0 overflow-hidden">
<div className="flex items-center gap-4 md:gap-8 lg:gap-12 mb-8 md:mb-12 lg:mb-16 flex-wrap justify-center"> <div
<div className="flex items-center"> className="absolute top-1/4 -left-20 w-100 h-100 rounded-full bg-linear-to-r from-primary/20 to-blue-400/20 blur-3xl"
<img style={{ transform: 'translate(10px, -5px)', opacity: 0.9 }}
src="images/imphnen-logo.svg" ></div>
alt="IMPHNEN" <div
className="h-12 md:h-16" className="absolute bottom-1/3 -right-20 w-100 h-100 rounded-full bg-linear-to-r from-blue-400/20 to-primary/20 blur-3xl"
/> style={{ transform: 'translate(0px, 0px)', opacity: 1 }}
</div> ></div>
<span className="text-3xl md:text-5xl lg:text-6xl font-bold text-gray-400"> <div className="absolute inset-0 bg-[linear-gradient(rgba(59,130,246,0.05)_1px,transparent_1px),linear-gradient(to_right,rgba(59,130,246,0.05)_1px,transparent_1px)] bg-size-[40px_40px]"></div>
×
</span>
<div className="flex items-center">
<img
src="images/sponsors/kolosal-logo_rlxbck.svg"
alt="Kolosal.ai"
className="h-8 md:h-12"
/>
</div>
</div> </div>
<div className="mx-auto container px-4 relative flex flex-col items-center">
{/* Title */} {/* Logos */}
<h1 className="text-h1 font-bold text-gray-900 mb-4 text-center"> <div className="flex items-center gap-4 md:gap-8 lg:gap-12 mb-8 md:mb-12 lg:mb-16 flex-wrap justify-center">
Hackathon <div className="flex items-center">
</h1> <img
src="images/imphnen-logo.svg"
{/* Subtitle */} alt="IMPHNEN"
<p className="text-p1 text-primary-500 font-semibold mb-6 md:mb-8 text-center px-4"> className="h-12 md:h-16"
"Inovasi AI: Mendorong Usaha Lokal dengan AI Inklusif" />
</p> </div>
<span className="text-3xl md:text-5xl font-bold text-gray-400">
{/* Description */} ×
<p className="text-p3 text-gray-600 max-w-lg text-center mb-8 md:mb-12 px-4"> </span>
Kompetisi pengembangan teknologi untuk menciptakan <div className="flex items-center">
<br className="hidden md:block" /> solusi inovatif yang menghadirkan <img
dampak nyata src="images/sponsors/kolosal-logo_rlxbck.svg"
</p> alt="Kolosal.ai"
className="h-8 md:h-12"
{/* Status */} />
<div className="flex items-center gap-4 md:gap-8 mb-10 md:mb-16 text-base text-gray-600"> </div>
<div className="flex items-center gap-2 md:gap-3">
<Icon icon="streamline-plump:web" width="16" height="16" />
<span>Online</span>
</div> </div>
<div className="flex items-center gap-2 md:gap-3 text-center"> {/* Title */}
<Icon icon="heroicons:clock" width="16" height="16" /> <h1 className="text-h1 font-bold text-gray-900 mb-4 text-center">
<span>Pendaftaran hingga 30 November 2025</span> Hackathon
</h1>
{/* Subtitle */}
<p className="text-p1 text-primary-500 font-semibold mb-6 md:mb-8 text-center px-4">
"Inovasi AI: Mendorong Usaha Lokal dengan AI Inklusif"
</p>
{/* Description */}
<p className="text-p3 text-gray-600 max-w-lg md:max-w-xl text-center mb-8 md:mb-12 px-4 font-sans">
Kompetisi pengembangan teknologi untuk menciptakan solusi inovatif
yang menghadirkan dampak nyata
</p>
{/* Status */}
<div className="flex items-center gap-4 md:gap-8 mb-10 md:mb-16 text-base text-gray-600">
<div className="flex items-center gap-2 md:gap-3">
<Icon icon="streamline-plump:web" width="16" height="16" />
<span>Online</span>
</div>
<div className="flex items-center gap-2 md:gap-3 text-center">
<Icon icon="heroicons:clock" width="16" height="16" />
<span>Pendaftaran hingga 30 November 2025</span>
</div>
</div>
{/* CTA Buttons */}
<div className="flex items-center gap-4 px-4">
<Button
onClick={() => navigate('/auth/signup')}
className="rounded-lg text-base max-h-auto"
>
Daftar Sekarang
</Button>
<a
href="https://chat.whatsapp.com/BlxrYh9uSC37d7VPhJslGL"
target="_blank"
rel="noopener noreferrer"
className="inline-flex items-center justify-center px-4 py-2.5 border-2 border-gray-300 hover:border-gray-400 transition-colors text-center bg-transparent hover:text-gray-900 hover:bg-gray-50 text-base text-gray-600 rounded-lg font-bai-jamjuree font-semibold"
>
Gabung Grup WA Hackathon
</a>
</div>
{/* Scroll indicator */}
<div className="mt-12 md:mt-20">
<svg
className="w-6 h-6 text-gray-400 animate-bounce"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M19 14l-7 7m0 0l-7-7m7 7V3"
/>
</svg>
</div> </div>
</div>
{/* CTA Buttons */}
<div className="flex items-center gap-4 px-4">
<Button
onClick={() => navigate('/auth/signup')}
className="rounded-lg text-base max-h-auto"
>
Daftar Sekarang
</Button>
<a
href="https://chat.whatsapp.com/BlxrYh9uSC37d7VPhJslGL"
target="_blank"
rel="noopener noreferrer"
className="inline-flex items-center justify-center px-4 py-2.5 border-2 border-gray-300 hover:border-gray-400 transition-colors text-center bg-transparent hover:text-gray-900 hover:bg-gray-50 text-base text-gray-600 rounded-lg font-bai-jamjuree font-semibold"
>
Gabung Grup WA Hackathon
</a>
</div>
{/* Scroll indicator */}
<div className="mt-12 md:mt-20">
<svg
className="w-6 h-6 text-gray-400 animate-bounce"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M19 14l-7 7m0 0l-7-7m7 7V3"
/>
</svg>
</div> </div>
</section> </section>
@@ -257,12 +266,12 @@ export default function HomePage() {
<h2 className="text-3xl md:text-5xl font-bold mb-10"> <h2 className="text-3xl md:text-5xl font-bold mb-10">
Tentang <span className="text-primary-500">Hackathon</span> Tentang <span className="text-primary-500">Hackathon</span>
</h2> </h2>
<p className="text-lg md:text-xl text-left md:text-center text-gray-600 mb-6"> <p className="font-sans text-lg md:text-xl text-left md:text-center text-gray-600 mb-6">
<span className="font-semibold text-gray-900">Hackathon</span>{' '} <span className="font-semibold text-gray-900">Hackathon</span>{' '}
adalah kompetisi pengembangan teknologi yang mengajak talenta muda adalah kompetisi pengembangan teknologi yang mengajak talenta muda
untuk berkolaborasi dalam menciptakan solusi inovatif. untuk berkolaborasi dalam menciptakan solusi inovatif.
</p> </p>
<p className="text-lg md:text-xl text-left md:text-center text-gray-600"> <p className="font-sans text-lg md:text-xl text-left md:text-center text-gray-600">
IMPHNEN bersama Kolosal.ai mengadakan Hackathon dengan tema lomba{' '} IMPHNEN bersama Kolosal.ai mengadakan Hackathon dengan tema lomba{' '}
<span className="font-semibold text-primary-500"> <span className="font-semibold text-primary-500">
"Inovasi AI: Mendorong Usaha Lokal dengan AI Inklusif" "Inovasi AI: Mendorong Usaha Lokal dengan AI Inklusif"
@@ -276,17 +285,17 @@ export default function HomePage() {
<section id="hadiah" className="py-16 md:py-24 px-4 md:px-8 bg-gray-50"> <section id="hadiah" className="py-16 md:py-24 px-4 md:px-8 bg-gray-50">
<div className="max-w-lg md:max-w-6xl mx-auto"> <div className="max-w-lg md:max-w-6xl mx-auto">
<div className="text-center mb-12"> <div className="text-center mb-12">
<h2 className="text-3xl md:text-5xl font-bold mb-4"> <h2 className="text-3xl md:text-5xl font-bold mb-4 font-bai-jamjuree">
Hadiah <span className="text-primary-500">Menarik</span> Hadiah <span className="text-primary-500">Menarik</span>
</h2> </h2>
<p className="text-xl md:text-2xl text-primary-500 font-semibold"> <p className="text-xl md:text-2xl text-primary-500 font-semibold font-sans">
Total Prize Pool Rp14.500.000 Total Prize Pool Rp14.500.000
</p> </p>
</div> </div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6"> <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
{/* Prize 1 */} {/* Prize 1 */}
<div className="bg-white rounded-xl p-8 shadow-lg border-2 border-gray-300 hover:border-primary-500 transition-colors"> <div className="bg-white rounded-xl px-4 py-8 shadow-lg border-2 border-gray-300 hover:border-primary-500 transition-colors">
<div className="flex justify-center mb-4"> <div className="flex justify-center mb-4">
<div className="w-16 h-16 bg-primary-100 rounded-full flex items-center justify-center"> <div className="w-16 h-16 bg-primary-100 rounded-full flex items-center justify-center">
<Icon <Icon
@@ -295,14 +304,14 @@ export default function HomePage() {
/> />
</div> </div>
</div> </div>
<h3 className="text-2xl font-bold text-center mb-2">Juara 1</h3> <h3 className="text-xl font-bold text-center mb-2">Juara 1</h3>
<p className="text-3xl font-bold text-primary-500 text-center"> <p className="text-2xl font-bold text-primary-500 text-center font-sans">
Rp6.000.000 Rp6.000.000
</p> </p>
</div> </div>
{/* Prize 2 */} {/* Prize 2 */}
<div className="bg-white rounded-xl p-8 shadow-lg border-2 border-gray-300 hover:border-gray-500 transition-colors"> <div className="bg-white rounded-xl px-4 py-8 shadow-lg border-2 border-gray-300 hover:border-gray-500 transition-colors">
<div className="flex justify-center mb-4"> <div className="flex justify-center mb-4">
<div className="w-16 h-16 bg-gray-100 rounded-full flex items-center justify-center"> <div className="w-16 h-16 bg-gray-100 rounded-full flex items-center justify-center">
<Icon <Icon
@@ -311,14 +320,14 @@ export default function HomePage() {
/> />
</div> </div>
</div> </div>
<h3 className="text-2xl font-bold text-center mb-2">Juara 2</h3> <h3 className="text-xl font-bold text-center mb-2">Juara 2</h3>
<p className="text-3xl font-bold text-primary-500 text-center"> <p className="text-2xl font-bold text-primary-500 text-center font-sans">
Rp4.000.000 Rp4.000.000
</p> </p>
</div> </div>
{/* Prize 3 */} {/* Prize 3 */}
<div className="bg-white rounded-xl p-8 shadow-lg border-2 border-gray-300 hover:border-orange-300 transition-colors"> <div className="bg-white rounded-xl px-4 py-8 shadow-lg border-2 border-gray-300 hover:border-orange-300 transition-colors">
<div className="flex justify-center mb-4"> <div className="flex justify-center mb-4">
<div className="w-16 h-16 bg-orange-100 rounded-full flex items-center justify-center"> <div className="w-16 h-16 bg-orange-100 rounded-full flex items-center justify-center">
<Icon <Icon
@@ -327,14 +336,14 @@ export default function HomePage() {
/> />
</div> </div>
</div> </div>
<h3 className="text-2xl font-bold text-center mb-2">Juara 3</h3> <h3 className="text-xl font-bold text-center mb-2">Juara 3</h3>
<p className="text-3xl font-bold text-orange-600 text-center"> <p className="text-2xl font-bold text-orange-600 text-center font-sans">
Rp2.500.000 Rp2.500.000
</p> </p>
</div> </div>
{/* Special Prize */} {/* Special Prize */}
<div className="bg-white rounded-xl p-8 shadow-lg border-2 border-gray-300 hover:border-purple-300 transition-colors"> <div className="bg-white rounded-xl px-4 py-8 shadow-lg border-2 border-gray-300 hover:border-purple-300 transition-colors">
<div className="flex justify-center mb-4"> <div className="flex justify-center mb-4">
<div className="w-16 h-16 bg-purple-100 rounded-full flex items-center justify-center"> <div className="w-16 h-16 bg-purple-100 rounded-full flex items-center justify-center">
<Icon <Icon
@@ -343,10 +352,10 @@ export default function HomePage() {
/> />
</div> </div>
</div> </div>
<h3 className="text-2xl font-bold text-center mb-2"> <h3 className="text-xl font-bold text-center mb-2">
Juara Kategori Lainnya Juara Kategori Lainnya
</h3> </h3>
<p className="text-3xl font-bold text-purple-600 text-center"> <p className="text-2xl font-bold text-purple-600 text-center font-sans">
Rp2.000.000 Rp2.000.000
</p> </p>
</div> </div>
@@ -356,8 +365,8 @@ export default function HomePage() {
{/* Timeline Section */} {/* Timeline Section */}
<section id="timeline" className="py-16 md:py-24 px-4 md:px-8 bg-white"> <section id="timeline" className="py-16 md:py-24 px-4 md:px-8 bg-white">
<div className="max-w-4xl mx-auto"> <div className="max-w-4xl mx-auto font-sans">
<div className="text-center mb-12"> <div className="text-center mb-12 font-bai-jamjuree">
<h2 className="text-3xl md:text-5xl font-bold mb-4"> <h2 className="text-3xl md:text-5xl font-bold mb-4">
Timeline <span className="text-primary-500">Acara</span> Timeline <span className="text-primary-500">Acara</span>
</h2> </h2>
@@ -456,9 +465,9 @@ export default function HomePage() {
</section> </section>
{/* Judges Section */} {/* Judges Section */}
<section className="py-16 md:py-24 px-4 md:px-8 bg-gray-50"> <section className="py-16 md:py-24 px-4 md:px-8 bg-gray-50 font-sans">
<div className="md:max-w-6xl mx-auto"> <div className="md:max-w-6xl mx-auto">
<div className="text-center mb-12"> <div className="text-center mb-12 font-bai-jamjuree">
<h2 className="text-3xl md:text-5xl font-bold mb-4"> <h2 className="text-3xl md:text-5xl font-bold mb-4">
Dewan <span className="text-primary-500">Juri</span> Dewan <span className="text-primary-500">Juri</span>
</h2> </h2>
@@ -469,7 +478,7 @@ export default function HomePage() {
<div className="w-full max-w-md md:max-w-4xl mx-auto grid grid-cols-1 md:grid-cols-3 gap-8"> <div className="w-full max-w-md md:max-w-4xl mx-auto grid grid-cols-1 md:grid-cols-3 gap-8">
{/* Judge 1 */} {/* Judge 1 */}
<div className="flex flex-col justify-between bg-white rounded-xl p-8 shadow-lg text-center"> <div className="flex flex-col justify-between bg-white rounded-xl px-4 py-8 shadow-lg text-center">
{/* <div className="w-24 h-24 bg-gray-200 rounded-full mx-auto mb-4"></div> */} {/* <div className="w-24 h-24 bg-gray-200 rounded-full mx-auto mb-4"></div> */}
<h3 className="text-p3 font-bold mb-1"> <h3 className="text-p3 font-bold mb-1">
Alifais Farrel Ramdhani Alifais Farrel Ramdhani
@@ -481,7 +490,7 @@ export default function HomePage() {
</div> </div>
{/* Judge 2 */} {/* Judge 2 */}
<div className="flex flex-col justify-between bg-white rounded-xl p-8 shadow-lg text-center"> <div className="flex flex-col justify-between bg-white rounded-xl px-4 py-8 shadow-lg text-center">
{/* <div className="w-24 h-24 bg-gray-200 rounded-full mx-auto mb-4"></div> */} {/* <div className="w-24 h-24 bg-gray-200 rounded-full mx-auto mb-4"></div> */}
<h3 className="text-p3 font-bold mb-1">Anka Tama</h3> <h3 className="text-p3 font-bold mb-1">Anka Tama</h3>
<div> <div>
@@ -491,7 +500,7 @@ export default function HomePage() {
</div> </div>
{/* Judge 3 */} {/* Judge 3 */}
<div className="flex flex-col justify-between bg-white rounded-xl p-8 shadow-lg text-center"> <div className="flex flex-col justify-between bg-white rounded-xl px-4 py-8 shadow-lg text-center">
{/* <div className="w-24 h-24 bg-gray-200 rounded-full mx-auto mb-4"></div> */} {/* <div className="w-24 h-24 bg-gray-200 rounded-full mx-auto mb-4"></div> */}
<h3 className="text-p3 font-bold mb-1">Hafid Nur</h3> <h3 className="text-p3 font-bold mb-1">Hafid Nur</h3>
<div> <div>
@@ -515,7 +524,7 @@ export default function HomePage() {
</p> </p>
</div> </div>
<div className="space-y-4"> <div className="space-y-4 font-sans">
{faqs.map((faq, index) => ( {faqs.map((faq, index) => (
<div key={index} className="border border-gray-200 rounded-lg"> <div key={index} className="border border-gray-200 rounded-lg">
<button <button
@@ -551,7 +560,7 @@ export default function HomePage() {
</section> </section>
{/* Sponsors Section */} {/* Sponsors Section */}
<section className="py-16 md:py-24 px-4 md:px-8 bg-gray-50"> <section className="py-20 md:py-28 px-4 md:px-8 bg-gray-50">
<div className="max-w-6xl mx-auto text-center"> <div className="max-w-6xl mx-auto text-center">
<h2 className="text-3xl md:text-5xl font-bold mb-4"> <h2 className="text-3xl md:text-5xl font-bold mb-4">
Sponsor & <span className="text-primary-500">Partner</span> Sponsor & <span className="text-primary-500">Partner</span>
@@ -580,10 +589,10 @@ export default function HomePage() {
{/* CTA Section */} {/* CTA Section */}
<section <section
id="masuk" id="masuk"
className="py-16 md:py-24 px-4 md:px-8 bg-linear-to-b from-white to-blue-50" className="py-20 md:py-28 px-4 md:px-8 bg-linear-to-b from-white to-blue-50"
> >
<div className="max-w-4xl mx-auto text-center"> <div className="max-w-4xl mx-auto text-center font-sans">
<h2 className="text-3xl md:text-5xl font-bold mb-6"> <h2 className="text-3xl md:text-5xl font-bold mb-6 font-bai-jamjuree">
Segera Daftarkan <span className="text-primary-500">Timmu!</span> Segera Daftarkan <span className="text-primary-500">Timmu!</span>
</h2> </h2>
<p className="text-lg md:text-xl text-left md:text-center text-gray-600 mb-8"> <p className="text-lg md:text-xl text-left md:text-center text-gray-600 mb-8">
@@ -620,12 +629,12 @@ export default function HomePage() {
</section> </section>
{/* Footer */} {/* Footer */}
<footer className="bg-gray-900 text-white py-12 px-4 md:px-8"> <footer className="bg-gray-900 text-white py-12 px-4 md:px-8 font-sans">
<div className="max-w-6xl mx-auto"> <div className="max-w-6xl mx-auto">
<div className="grid grid-cols-1 md:grid-cols-3 gap-8 mb-8"> <div className="grid grid-cols-1 md:grid-cols-3 gap-8 mb-8">
{/* Brand */} {/* Brand */}
<div> <div>
<div className="flex items-center gap-2 mb-4"> <div className="flex items-center gap-2 mb-4 font-bai-jamjuree">
<span className="text-2xl font-bold">IMPHNEN</span> <span className="text-2xl font-bold">IMPHNEN</span>
<span className="text-2xl font-bold text-blue-500"> <span className="text-2xl font-bold text-blue-500">
Hackathon Hackathon
@@ -689,7 +698,9 @@ export default function HomePage() {
{/* Quick Links */} {/* Quick Links */}
<div> <div>
<h3 className="font-bold text-lg mb-4">Quick Links</h3> <h3 className="font-bold text-lg mb-4 font-bai-jamjuree">
Quick Links
</h3>
<ul className="space-y-2 text-gray-400"> <ul className="space-y-2 text-gray-400">
<li> <li>
<a <a
@@ -725,7 +736,9 @@ export default function HomePage() {
{/* Contact */} {/* Contact */}
<div> <div>
<h3 className="font-bold text-lg mb-4">Contact</h3> <h3 className="font-bold text-lg mb-4 font-bai-jamjuree">
Contact
</h3>
<ul className="space-y-2 text-gray-400 text-sm"> <ul className="space-y-2 text-gray-400 text-sm">
<li className="flex items-start gap-2"> <li className="flex items-start gap-2">
<Icon <Icon