Files
grimlock/frontend/src/app/(auth)/register/page.tsx
JA 22fe893e65 Frontend: Complete Next.js implementation
Core Features:
-  Authentication (login/register pages)
-  Main layout with sidebar
-  Channel list and navigation
-  Channel page with real-time messaging
-  Direct messages (DMs) page
-  Message components (list, input)
-  WebSocket integration for real-time updates
-  Typing indicators
-  Online status indicators
-  Stores (auth, channels, messages)
-  API client with JWT auth
-  Socket.IO client wrapper
-  Responsive UI with Tailwind

Tech Stack:
- Next.js 14 (App Router)
- TypeScript
- TailwindCSS
- Socket.IO client
- Zustand (state management)
- Axios (HTTP client)
- date-fns (date formatting)
- lucide-react (icons)

Build Status:  Successfully compiles
Production Ready:  Optimized build generated
2026-02-13 16:34:49 +00:00

177 lines
6.8 KiB
TypeScript

'use client';
import { useState } from 'react';
import { useRouter } from 'next/navigation';
import Link from 'next/link';
import { useAuthStore } from '@/stores/useAuthStore';
export default function RegisterPage() {
const router = useRouter();
const { register, isLoading, error, clearError } = useAuthStore();
const [formData, setFormData] = useState({
email: '',
name: '',
password: '',
confirmPassword: '',
role: 'engineer' as 'engineer' | 'BD' | 'admin' | 'exec',
});
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
clearError();
if (formData.password !== formData.confirmPassword) {
alert('Passwords do not match');
return;
}
try {
await register({
email: formData.email,
name: formData.name,
password: formData.password,
role: formData.role,
});
router.push('/channels');
} catch (err) {
// Error is handled by the store
}
};
const handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLSelectElement>) => {
setFormData(prev => ({
...prev,
[e.target.name]: e.target.value,
}));
};
return (
<div className="min-h-screen flex items-center justify-center bg-gray-50 dark:bg-gray-900 py-12 px-4 sm:px-6 lg:px-8">
<div className="max-w-md w-full space-y-8">
<div>
<h1 className="text-center text-4xl font-bold text-gray-900 dark:text-white">
Grimlock
</h1>
<h2 className="mt-6 text-center text-3xl font-extrabold text-gray-900 dark:text-white">
Create your account
</h2>
<p className="mt-2 text-center text-sm text-gray-600 dark:text-gray-400">
Or{' '}
<Link href="/login" className="font-medium text-blue-600 hover:text-blue-500">
sign in to existing account
</Link>
</p>
</div>
<form className="mt-8 space-y-6" onSubmit={handleSubmit}>
{error && (
<div className="rounded-md bg-red-50 dark:bg-red-900/20 p-4">
<div className="text-sm text-red-800 dark:text-red-200">
{error}
</div>
</div>
)}
<div className="space-y-4">
<div>
<label htmlFor="name" className="block text-sm font-medium text-gray-700 dark:text-gray-300">
Full Name
</label>
<input
id="name"
name="name"
type="text"
required
value={formData.name}
onChange={handleChange}
className="mt-1 appearance-none relative block w-full px-3 py-2 border border-gray-300 dark:border-gray-700 placeholder-gray-500 dark:placeholder-gray-400 text-gray-900 dark:text-white dark:bg-gray-800 rounded-md focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
placeholder="John Doe"
/>
</div>
<div>
<label htmlFor="email" className="block text-sm font-medium text-gray-700 dark:text-gray-300">
Email address
</label>
<input
id="email"
name="email"
type="email"
autoComplete="email"
required
value={formData.email}
onChange={handleChange}
className="mt-1 appearance-none relative block w-full px-3 py-2 border border-gray-300 dark:border-gray-700 placeholder-gray-500 dark:placeholder-gray-400 text-gray-900 dark:text-white dark:bg-gray-800 rounded-md focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
placeholder="you@example.com"
/>
</div>
<div>
<label htmlFor="role" className="block text-sm font-medium text-gray-700 dark:text-gray-300">
Role
</label>
<select
id="role"
name="role"
value={formData.role}
onChange={handleChange}
className="mt-1 block w-full px-3 py-2 border border-gray-300 dark:border-gray-700 text-gray-900 dark:text-white dark:bg-gray-800 rounded-md focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
>
<option value="engineer">Engineer</option>
<option value="BD">Business Development</option>
<option value="admin">Admin</option>
<option value="exec">Executive</option>
</select>
</div>
<div>
<label htmlFor="password" className="block text-sm font-medium text-gray-700 dark:text-gray-300">
Password
</label>
<input
id="password"
name="password"
type="password"
autoComplete="new-password"
required
value={formData.password}
onChange={handleChange}
className="mt-1 appearance-none relative block w-full px-3 py-2 border border-gray-300 dark:border-gray-700 placeholder-gray-500 dark:placeholder-gray-400 text-gray-900 dark:text-white dark:bg-gray-800 rounded-md focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
placeholder="••••••••"
/>
</div>
<div>
<label htmlFor="confirmPassword" className="block text-sm font-medium text-gray-700 dark:text-gray-300">
Confirm Password
</label>
<input
id="confirmPassword"
name="confirmPassword"
type="password"
autoComplete="new-password"
required
value={formData.confirmPassword}
onChange={handleChange}
className="mt-1 appearance-none relative block w-full px-3 py-2 border border-gray-300 dark:border-gray-700 placeholder-gray-500 dark:placeholder-gray-400 text-gray-900 dark:text-white dark:bg-gray-800 rounded-md focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
placeholder="••••••••"
/>
</div>
</div>
<div>
<button
type="submit"
disabled={isLoading}
className="group relative w-full flex justify-center py-2 px-4 border border-transparent text-sm font-medium rounded-md text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 disabled:bg-blue-400 disabled:cursor-not-allowed"
>
{isLoading ? 'Creating account...' : 'Create account'}
</button>
</div>
</form>
</div>
</div>
);
}