Initial commit: Kottgard production website code
This commit is contained in:
@@ -0,0 +1,384 @@
|
||||
/**
|
||||
* ANNOTATED COPY — every line explained
|
||||
* Source: src/app/login/page.tsx
|
||||
* NOT used by the app — read this to learn how the real file works
|
||||
*/
|
||||
// Next.js: this file runs in the BROWSER (needs useState, onClick, localStorage, etc.)
|
||||
'use client';
|
||||
// (blank line — separates logical blocks for readability)
|
||||
// Import React — core UI library (components, hooks, JSX)
|
||||
import { useState } from 'react';
|
||||
// Next.js Link — fast client-side navigation without full page reload
|
||||
import Link from 'next/link';
|
||||
// Next.js App Router hooks — useRouter, useParams, useSearchParams
|
||||
import { useRouter } from 'next/navigation';
|
||||
// Import project module (@/ alias = src/ folder in tsconfig)
|
||||
import Button from '@/components/ui/Button';
|
||||
// Import project module (@/ alias = src/ folder in tsconfig)
|
||||
import { useAuthStore } from '@/store/auth';
|
||||
// Import project module (@/ alias = src/ folder in tsconfig)
|
||||
import { useTranslation } from '@/hooks/useTranslation';
|
||||
// Import project module (@/ alias = src/ folder in tsconfig)
|
||||
import { DEMO_EMAIL } from '@/lib/constants';
|
||||
// (blank line — separates logical blocks for readability)
|
||||
// Default export — main component/page Next.js or other files import
|
||||
export default function LoginPage() {
|
||||
// Next.js router — programmatic navigation (router.push)
|
||||
const router = useRouter();
|
||||
// Zustand selector — subscribe to slice of global store
|
||||
const { login, register } = useAuthStore();
|
||||
// Custom hook — returns t() translator and current locale
|
||||
const { t } = useTranslation();
|
||||
// React useState — local component state that triggers re-render on change
|
||||
const [isRegister, setIsRegister] = useState(false);
|
||||
// React useState — local component state that triggers re-render on change
|
||||
const [error, setError] = useState('');
|
||||
// React useState — local component state that triggers re-render on change
|
||||
const [form, setForm] = useState({
|
||||
// Property or array item — trailing comma allowed in TypeScript
|
||||
name: '',
|
||||
// Property or array item — trailing comma allowed in TypeScript
|
||||
email: '',
|
||||
// Property or array item — trailing comma allowed in TypeScript
|
||||
password: '',
|
||||
// Property or array item — trailing comma allowed in TypeScript
|
||||
phone: '',
|
||||
// Property or array item — trailing comma allowed in TypeScript
|
||||
street: '',
|
||||
// Property or array item — trailing comma allowed in TypeScript
|
||||
city: '',
|
||||
// Property or array item — trailing comma allowed in TypeScript
|
||||
state: '',
|
||||
// Property or array item — trailing comma allowed in TypeScript
|
||||
zip: '',
|
||||
// Closing brace — end of block (function, if, object, JSX)
|
||||
});
|
||||
// (blank line — separates logical blocks for readability)
|
||||
// Line 28: const handleSubmit = (e: React.FormEvent) => {
|
||||
const handleSubmit = (e: React.FormEvent) => {
|
||||
// Line 29: e.preventDefault();
|
||||
e.preventDefault();
|
||||
// Line 30: setError('');
|
||||
setError('');
|
||||
// (blank line — separates logical blocks for readability)
|
||||
// Conditional branch — different behavior based on runtime value
|
||||
if (isRegister) {
|
||||
// Line 33: const success = register({
|
||||
const success = register({
|
||||
// Property or array item — trailing comma allowed in TypeScript
|
||||
name: form.name,
|
||||
// Property or array item — trailing comma allowed in TypeScript
|
||||
email: form.email,
|
||||
// Property or array item — trailing comma allowed in TypeScript
|
||||
password: form.password,
|
||||
// Property or array item — trailing comma allowed in TypeScript
|
||||
phone: form.phone,
|
||||
// Line 38: address: {
|
||||
address: {
|
||||
// Property or array item — trailing comma allowed in TypeScript
|
||||
street: form.street,
|
||||
// Property or array item — trailing comma allowed in TypeScript
|
||||
city: form.city,
|
||||
// Property or array item — trailing comma allowed in TypeScript
|
||||
state: form.state,
|
||||
// Property or array item — trailing comma allowed in TypeScript
|
||||
zip: form.zip,
|
||||
// Closing brace — end of block (function, if, object, JSX)
|
||||
},
|
||||
// Closing brace — end of block (function, if, object, JSX)
|
||||
});
|
||||
// Conditional branch — different behavior based on runtime value
|
||||
if (success) router.push('/account');
|
||||
// Closing brace — end of block (function, if, object, JSX)
|
||||
} else {
|
||||
// Line 47: const success = login(form.email, form.password);
|
||||
const success = login(form.email, form.password);
|
||||
// Conditional branch — different behavior based on runtime value
|
||||
if (success) {
|
||||
// Line 49: router.push('/account');
|
||||
router.push('/account');
|
||||
// Closing brace — end of block (function, if, object, JSX)
|
||||
} else {
|
||||
// Line 51: setError(t('auth.invalidCredentials', { email: DEMO_EMAIL...
|
||||
setError(t('auth.invalidCredentials', { email: DEMO_EMAIL }));
|
||||
// Closing brace — end of block (function, if, object, JSX)
|
||||
}
|
||||
// Closing brace — end of block (function, if, object, JSX)
|
||||
}
|
||||
// Closing brace — end of block (function, if, object, JSX)
|
||||
};
|
||||
// (blank line — separates logical blocks for readability)
|
||||
// Return JSX — describes UI tree React renders to the DOM
|
||||
return (
|
||||
// JSX element — HTML-like tag becomes React component in browser
|
||||
<div className="flex min-h-[70vh] items-center justify-center bg-gray-50 px-4 py-12">
|
||||
// JSX element — HTML-like tag becomes React component in browser
|
||||
<div className="w-full max-w-md">
|
||||
// JSX element — HTML-like tag becomes React component in browser
|
||||
<div className="mb-8 text-center">
|
||||
// JSX element — HTML-like tag becomes React component in browser
|
||||
<div className="mx-auto mb-4 flex h-14 w-14 items-center justify-center rounded-full bg-brand-700">
|
||||
// JSX element — HTML-like tag becomes React component in browser
|
||||
<span className="font-display text-xl font-bold text-gold-400">
|
||||
// Line 62: {t('site.initials')}
|
||||
{t('site.initials')}
|
||||
// JSX element — HTML-like tag becomes React component in browser
|
||||
</span>
|
||||
// JSX element — HTML-like tag becomes React component in browser
|
||||
</div>
|
||||
// JSX element — HTML-like tag becomes React component in browser
|
||||
<h1 className="font-display text-2xl font-bold text-brand-900">
|
||||
// Line 66: {isRegister ? t('auth.createAccount') : t('auth.welcomeBa...
|
||||
{isRegister ? t('auth.createAccount') : t('auth.welcomeBack')}
|
||||
// JSX element — HTML-like tag becomes React component in browser
|
||||
</h1>
|
||||
// JSX element — HTML-like tag becomes React component in browser
|
||||
<p className="mt-1 text-sm text-gray-500">
|
||||
// Line 69: {isRegister
|
||||
{isRegister
|
||||
// Line 70: ? t('auth.joinTagline', { name: t('site.name') })
|
||||
? t('auth.joinTagline', { name: t('site.name') })
|
||||
// Line 71: : t('auth.signInTagline', { name: t('site.name') })}
|
||||
: t('auth.signInTagline', { name: t('site.name') })}
|
||||
// JSX element — HTML-like tag becomes React component in browser
|
||||
</p>
|
||||
// JSX element — HTML-like tag becomes React component in browser
|
||||
</div>
|
||||
// (blank line — separates logical blocks for readability)
|
||||
// JSX element — HTML-like tag becomes React component in browser
|
||||
<div className="card-premium p-8">
|
||||
// JSX element — HTML-like tag becomes React component in browser
|
||||
<form onSubmit={handleSubmit} className="space-y-4">
|
||||
// Line 77: {isRegister && (
|
||||
{isRegister && (
|
||||
// JSX element — HTML-like tag becomes React component in browser
|
||||
<div>
|
||||
// JSX element — HTML-like tag becomes React component in browser
|
||||
<label className="label-text">{t('auth.fullName')}</label>
|
||||
// JSX element — HTML-like tag becomes React component in browser
|
||||
<input
|
||||
// Line 81: required
|
||||
required
|
||||
// Tailwind CSS utility classes — styling (colors, spacing, layout)
|
||||
className="input-field"
|
||||
// Line 83: value={form.name}
|
||||
value={form.name}
|
||||
// Change handler — runs when input/select value changes
|
||||
onChange={(e) => setForm({ ...form, name: e.target.value })}
|
||||
// Line 85: />
|
||||
/>
|
||||
// JSX element — HTML-like tag becomes React component in browser
|
||||
</div>
|
||||
// Line 87: )}
|
||||
)}
|
||||
// (blank line — separates logical blocks for readability)
|
||||
// JSX element — HTML-like tag becomes React component in browser
|
||||
<div>
|
||||
// JSX element — HTML-like tag becomes React component in browser
|
||||
<label className="label-text">{t('auth.email')}</label>
|
||||
// JSX element — HTML-like tag becomes React component in browser
|
||||
<input
|
||||
// Line 92: required
|
||||
required
|
||||
// Line 93: type="email"
|
||||
type="email"
|
||||
// Tailwind CSS utility classes — styling (colors, spacing, layout)
|
||||
className="input-field"
|
||||
// Line 95: value={form.email}
|
||||
value={form.email}
|
||||
// Change handler — runs when input/select value changes
|
||||
onChange={(e) => setForm({ ...form, email: e.target.value })}
|
||||
// Line 97: />
|
||||
/>
|
||||
// JSX element — HTML-like tag becomes React component in browser
|
||||
</div>
|
||||
// (blank line — separates logical blocks for readability)
|
||||
// JSX element — HTML-like tag becomes React component in browser
|
||||
<div>
|
||||
// JSX element — HTML-like tag becomes React component in browser
|
||||
<label className="label-text">{t('auth.password')}</label>
|
||||
// JSX element — HTML-like tag becomes React component in browser
|
||||
<input
|
||||
// Line 103: required
|
||||
required
|
||||
// Line 104: type="password"
|
||||
type="password"
|
||||
// Tailwind CSS utility classes — styling (colors, spacing, layout)
|
||||
className="input-field"
|
||||
// Line 106: value={form.password}
|
||||
value={form.password}
|
||||
// Change handler — runs when input/select value changes
|
||||
onChange={(e) => setForm({ ...form, password: e.target.value })}
|
||||
// Line 108: />
|
||||
/>
|
||||
// JSX element — HTML-like tag becomes React component in browser
|
||||
</div>
|
||||
// (blank line — separates logical blocks for readability)
|
||||
// Line 111: {isRegister && (
|
||||
{isRegister && (
|
||||
// React Fragment — group elements without extra wrapper DOM node
|
||||
<>
|
||||
// JSX element — HTML-like tag becomes React component in browser
|
||||
<div>
|
||||
// JSX element — HTML-like tag becomes React component in browser
|
||||
<label className="label-text">{t('auth.phone')}</label>
|
||||
// JSX element — HTML-like tag becomes React component in browser
|
||||
<input
|
||||
// Line 116: required
|
||||
required
|
||||
// Line 117: type="tel"
|
||||
type="tel"
|
||||
// Tailwind CSS utility classes — styling (colors, spacing, layout)
|
||||
className="input-field"
|
||||
// Line 119: value={form.phone}
|
||||
value={form.phone}
|
||||
// Change handler — runs when input/select value changes
|
||||
onChange={(e) => setForm({ ...form, phone: e.target.value })}
|
||||
// Line 121: />
|
||||
/>
|
||||
// JSX element — HTML-like tag becomes React component in browser
|
||||
</div>
|
||||
// JSX element — HTML-like tag becomes React component in browser
|
||||
<div>
|
||||
// JSX element — HTML-like tag becomes React component in browser
|
||||
<label className="label-text">{t('auth.street')}</label>
|
||||
// JSX element — HTML-like tag becomes React component in browser
|
||||
<input
|
||||
// Line 126: required
|
||||
required
|
||||
// Tailwind CSS utility classes — styling (colors, spacing, layout)
|
||||
className="input-field"
|
||||
// Line 128: value={form.street}
|
||||
value={form.street}
|
||||
// Change handler — runs when input/select value changes
|
||||
onChange={(e) => setForm({ ...form, street: e.target.value })}
|
||||
// Line 130: />
|
||||
/>
|
||||
// JSX element — HTML-like tag becomes React component in browser
|
||||
</div>
|
||||
// JSX element — HTML-like tag becomes React component in browser
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
// JSX element — HTML-like tag becomes React component in browser
|
||||
<div>
|
||||
// JSX element — HTML-like tag becomes React component in browser
|
||||
<label className="label-text">{t('auth.city')}</label>
|
||||
// JSX element — HTML-like tag becomes React component in browser
|
||||
<input
|
||||
// Line 136: required
|
||||
required
|
||||
// Tailwind CSS utility classes — styling (colors, spacing, layout)
|
||||
className="input-field"
|
||||
// Line 138: value={form.city}
|
||||
value={form.city}
|
||||
// Change handler — runs when input/select value changes
|
||||
onChange={(e) => setForm({ ...form, city: e.target.value })}
|
||||
// Line 140: />
|
||||
/>
|
||||
// JSX element — HTML-like tag becomes React component in browser
|
||||
</div>
|
||||
// JSX element — HTML-like tag becomes React component in browser
|
||||
<div>
|
||||
// JSX element — HTML-like tag becomes React component in browser
|
||||
<label className="label-text">{t('auth.state')}</label>
|
||||
// JSX element — HTML-like tag becomes React component in browser
|
||||
<input
|
||||
// Line 145: required
|
||||
required
|
||||
// Tailwind CSS utility classes — styling (colors, spacing, layout)
|
||||
className="input-field"
|
||||
// Line 147: value={form.state}
|
||||
value={form.state}
|
||||
// Change handler — runs when input/select value changes
|
||||
onChange={(e) => setForm({ ...form, state: e.target.value })}
|
||||
// Line 149: />
|
||||
/>
|
||||
// JSX element — HTML-like tag becomes React component in browser
|
||||
</div>
|
||||
// JSX element — HTML-like tag becomes React component in browser
|
||||
</div>
|
||||
// JSX element — HTML-like tag becomes React component in browser
|
||||
<div>
|
||||
// JSX element — HTML-like tag becomes React component in browser
|
||||
<label className="label-text">{t('auth.zip')}</label>
|
||||
// JSX element — HTML-like tag becomes React component in browser
|
||||
<input
|
||||
// Line 155: required
|
||||
required
|
||||
// Tailwind CSS utility classes — styling (colors, spacing, layout)
|
||||
className="input-field"
|
||||
// Line 157: value={form.zip}
|
||||
value={form.zip}
|
||||
// Change handler — runs when input/select value changes
|
||||
onChange={(e) => setForm({ ...form, zip: e.target.value })}
|
||||
// Line 159: />
|
||||
/>
|
||||
// JSX element — HTML-like tag becomes React component in browser
|
||||
</div>
|
||||
// JSX element — HTML-like tag becomes React component in browser
|
||||
</>
|
||||
// Line 162: )}
|
||||
)}
|
||||
// (blank line — separates logical blocks for readability)
|
||||
// Line 164: {error && (
|
||||
{error && (
|
||||
// JSX element — HTML-like tag becomes React component in browser
|
||||
<p className="rounded-lg bg-red-50 px-4 py-2 text-sm text-red-600">{error}</p>
|
||||
// Line 166: )}
|
||||
)}
|
||||
// (blank line — separates logical blocks for readability)
|
||||
// JSX element — HTML-like tag becomes React component in browser
|
||||
<Button type="submit" variant="primary" size="lg" className="w-full">
|
||||
// Line 169: {isRegister ? t('auth.register') : t('auth.signIn')}
|
||||
{isRegister ? t('auth.register') : t('auth.signIn')}
|
||||
// JSX element — HTML-like tag becomes React component in browser
|
||||
</Button>
|
||||
// JSX element — HTML-like tag becomes React component in browser
|
||||
</form>
|
||||
// (blank line — separates logical blocks for readability)
|
||||
// JSX element — HTML-like tag becomes React component in browser
|
||||
<div className="mt-6 text-center">
|
||||
// JSX element — HTML-like tag becomes React component in browser
|
||||
<button
|
||||
// Click handler — runs when user clicks (must be client component)
|
||||
onClick={() => {
|
||||
// Line 176: setIsRegister(!isRegister);
|
||||
setIsRegister(!isRegister);
|
||||
// Line 177: setError('');
|
||||
setError('');
|
||||
// Closing brace — end of block (function, if, object, JSX)
|
||||
}}
|
||||
// Tailwind CSS utility classes — styling (colors, spacing, layout)
|
||||
className="text-sm font-medium text-brand-600 hover:text-brand-800"
|
||||
// Line 180: >
|
||||
>
|
||||
// Line 181: {isRegister ? t('auth.hasAccount') : t('auth.noAccount')}
|
||||
{isRegister ? t('auth.hasAccount') : t('auth.noAccount')}
|
||||
// JSX element — HTML-like tag becomes React component in browser
|
||||
</button>
|
||||
// JSX element — HTML-like tag becomes React component in browser
|
||||
</div>
|
||||
// (blank line — separates logical blocks for readability)
|
||||
// Line 185: {!isRegister && (
|
||||
{!isRegister && (
|
||||
// JSX element — HTML-like tag becomes React component in browser
|
||||
<div className="mt-4 rounded-lg bg-brand-50 px-4 py-3 text-center">
|
||||
// JSX element — HTML-like tag becomes React component in browser
|
||||
<p className="text-xs text-brand-700">
|
||||
// Line 188: {t('auth.demo', { email: DEMO_EMAIL })}
|
||||
{t('auth.demo', { email: DEMO_EMAIL })}
|
||||
// JSX element — HTML-like tag becomes React component in browser
|
||||
</p>
|
||||
// JSX element — HTML-like tag becomes React component in browser
|
||||
</div>
|
||||
// Line 191: )}
|
||||
)}
|
||||
// JSX element — HTML-like tag becomes React component in browser
|
||||
</div>
|
||||
// JSX element — HTML-like tag becomes React component in browser
|
||||
</div>
|
||||
// JSX element — HTML-like tag becomes React component in browser
|
||||
</div>
|
||||
// Line 195: );
|
||||
);
|
||||
// Closing brace — end of block (function, if, object, JSX)
|
||||
}
|
||||
Reference in New Issue
Block a user