Feature: Add password change functionality for admin and customers

- Add customer password change page at /account/change-password
- Add admin password change page at /admin/change-password
- Create AdminUser database table for storing admin credentials
- Admin login now checks database first, falls back to env vars
- Update admin auth to support bcrypt password hashing
- Add change password links to customer account and admin nav
- Fix customer login logs back link (was pointing to non-existent /admin)
- Add success/error message handling for password changes

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
Andymick
2026-07-19 09:51:52 +01:00
co-authored by Claude Haiku 4.5
parent 36cc6dddff
commit 9623ac2fdb
14 changed files with 430 additions and 29 deletions
+83
View File
@@ -0,0 +1,83 @@
import Link from 'next/link';
import { redirect } from 'next/navigation';
import { isAdminAuthenticated } from '@/lib/adminAuth';
import { changeAdminPassword } from './actions';
const ERROR_MESSAGES: Record<string, string> = {
missing_fields: 'Please fill in all fields.',
password_too_short: 'Password must be at least 8 characters.',
passwords_dont_match: 'Passwords do not match.',
invalid_current: 'Current password is incorrect.',
};
export default async function ChangeAdminPasswordPage({ searchParams }: { searchParams: { error?: string } }) {
const isAuthenticated = await isAdminAuthenticated();
if (!isAuthenticated) redirect('/admin/login');
const error = searchParams.error ? ERROR_MESSAGES[searchParams.error] : null;
return (
<div className="mx-auto max-w-2xl px-6 py-12">
<Link href="/admin/orders" className="tag-label text-muted hover:text-clay">
Back to orders
</Link>
<h1 className="mt-6 font-display text-3xl">Change admin password</h1>
<p className="mt-2 text-muted">Update your admin account password</p>
{error && (
<p className="mt-6 border border-splash-pink/40 bg-splash-pink/10 px-4 py-3 text-sm text-splash-pink">
{error}
</p>
)}
<form action={changeAdminPassword} className="mt-8 max-w-md space-y-6">
<div>
<label htmlFor="current" className="tag-label mb-2 block">
Current password
</label>
<input
id="current"
name="currentPassword"
type="password"
required
className="w-full border border-line bg-paper px-3 py-2 text-sm"
/>
</div>
<div>
<label htmlFor="new" className="tag-label mb-2 block">
New password
</label>
<input
id="new"
name="newPassword"
type="password"
required
minLength={8}
className="w-full border border-line bg-paper px-3 py-2 text-sm"
/>
<p className="mt-1 text-xs text-muted">At least 8 characters</p>
</div>
<div>
<label htmlFor="confirm" className="tag-label mb-2 block">
Confirm new password
</label>
<input
id="confirm"
name="confirmPassword"
type="password"
required
minLength={8}
className="w-full border border-line bg-paper px-3 py-2 text-sm"
/>
</div>
<button type="submit" className="bg-clay px-6 py-2 text-sm font-medium text-paper hover:bg-clay-dark">
Update password
</button>
</form>
</div>
);
}